7.5. simple-persist模块

该模块定义了两个简单的数据访问对象(DAO)。一个DAO是一个提供持久化操作接口的对象。在这个应用中我们使用了对象关系映射(ORM)框架Hibernate,DAO通常在对象旁边定义。在本项目中,我们定义两个DAO对象:WeatherDAOLocationDAOWeatherDAO类允许我们保存一个Weather对象至数据库,根据id获取一个Weather对象,获取匹配特定LocationWeather对象。LocationDAO有一个方法允许我们根据邮政编码获取Location对象。首先,让我们看一下Example 7.8, “simple-persist 的 POM”中的simple-persist POM。

Example 7.8. simple-persist 的 POM

<project xmlns="http://maven.apache.org/POM/4.0.0" 
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
                      http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>org.sonatype.mavenbook.ch07</groupId>
    <artifactId>simple-parent</artifactId>
    <version>1.0</version>
  </parent>
  <artifactId>simple-persist</artifactId>
  <packaging>jar</packaging>

  <name>Simple Persistence API</name>

  <dependencies>
    <dependency>
      <groupId>org.sonatype.mavenbook.ch07</groupId>
      <artifactId>simple-model</artifactId>
      <version>1.0</version>
    </dependency>
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate</artifactId>
      <version>3.2.5.ga</version>
      <exclusions>
        <exclusion>
          <groupId>javax.transaction</groupId>
          <artifactId>jta</artifactId>
        </exclusion>
      </exclusions>
    </dependency>
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-annotations</artifactId>
      <version>3.3.0.ga</version>
    </dependency>
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-commons-annotations</artifactId>
      <version>3.3.0.ga</version>
    </dependency>
    <dependency>
      <groupId>org.apache.geronimo.specs</groupId>
      <artifactId>geronimo-jta_1.1_spec</artifactId>
      <version>1.1</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring</artifactId>
      <version>2.0.7</version>
    </dependency>
  </dependencies>
</project>

这个POM文件引用simple-parent作为一个父POM,它定义了一些依赖。simple-persistPOM中列出的依赖有:

org.sonatype.mavenbook.ch07:simple-model:1.0

就像simple-weather模块一样,这个持久化模块引用了simple-model中定义的核心模型对象。

org.hibernate:hibernate:3.2.5.ga

我们定义了一个对HIbernate版本3.2.5ga的依赖,但注意我们排除了Hibernate的一个依赖。这么做是因为javax.transaction:javax依赖在公共Maven仓库中不可用。此依赖正好是Sun依赖中的一个,不能免费在中央Maven仓库中提供。为了避免烦人的信息告诉我们去下载非免费的依赖,我们简单的从Hibernate排除这个依赖然后添加一个geronimo-jta_1.1_spec依赖。

org.apache.geronimo.specs:geronimo-jta_1.1_spec:1.1

就像Servlet和JSP API,Apache Geronimo项目也根据Apache许可证友好的发布了一些认证过的企业级API。这意味着不管什么时候某个组件告诉你它依赖于JDBCJNDI,和JTA API,你都可以查一下groupId为org.apache.geronimo.specs下的对应类库。

org.springframework:spring:2.0.7

这里包含了整个Spring Framework作为一个依赖

Note

只依赖于你正使用的Spring组件是一个很好的实践。Spring Framework项目很友好的创建了一些有针对性的构件如spring-hibernate3

为什么依赖于Spring呢?当和Hibernate集成的时候,Spring允许我们使用一些帮助类如HibernateDaoSupport。作为一个HibernateDaoSupport的样例,看一下Example 7.9, “simple-persist'的WeatherDAO类”中的WeatherDAO代码。

Example 7.9. simple-persist'的WeatherDAO类

package org.sonatype.mavenbook.weather.persist;

import java.util.ArrayList;
import java.util.List;

import org.hibernate.Query;
import org.hibernate.Session;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

import org.sonatype.mavenbook.weather.model.Location;
import org.sonatype.mavenbook.weather.model.Weather;

public class WeatherDAO extends HibernateDaoSupport {

    public WeatherDAO() {}

    public void save(Weather weather) {
      getHibernateTemplate().save( weather );
    }

    public Weather load(Integer id) {
      return (Weather) getHibernateTemplate().load( Weather.class, id);
    }

    @SuppressWarnings("unchecked")
    public List<Weather> recentForLocation( final Location location ) {
      return (List<Weather>) getHibernateTemplate().execute(
        new HibernateCallback() {
        public Object doInHibernate(Session session) {
          Query query = getSession().getNamedQuery("Weather.byLocation");
          query.setParameter("location", location);
          return new ArrayList<Weather>( query.list() );
        }
      });
    }
}

就是这样。你已经编写了一个类,它能插入新的行,根据主键选取,以及能查找所有Weather表根据id连接Location表的结果。很显然,我们不能将书停在这里,然后花500页给你解释Hibernate的运作详情,但我们能做一些快速简单的解释:

1

继承HibernateDaoSupport的类。这个类会和Hibernate SessionFactory关联,后者将被用来创建Hibernate Session对象。在Hibernate中,每个操作都涉及Session对象,一个Session是访问下层数据库的中介,它也负责管理对DataSource的JDBC连接。继承HibernateDaoSupport也意味着我们能够使用getHibernateTemplate()访问HibernateTemplate。能使用HibernateTemplate完成的操作例子有……

2

save()方法接受一个Weather实例然后调用HibernateTemplate上的save()方法。HibernateTemplate简化了常见的HIbernate操作的调用,并别将所有数据库特有的异常转换成了运行时异常。这里我们调用save(),它往Weather表中插入一条新的记录。可选的操作有update(),它更新已存在的一行,或者saveOrUpdate(),它会根据Weather中的non-null id属性是否存在,执行保存或者更新。

3

load()方法,同样,也只是调用HibernateTemplate实例的方法。HibernateTemplate上的load()接受一个Class对象和一个Serializable对象。本例中,Serializable对应于要载入的Weather对象的id的值。

4

最后一个方法recentForLocation()调用定义在Weather模型对象中的NamedQuery。如果你的记忆力足够好,你就知道Weather模型对象定义了一个命名查询"Weather.byLocation",查询为"from Weather w where w.location = :location"。我们通过HibernateCallback中的Hibernate Session对象来载入NamedQueryHibernateCallbackHibernateTemplateexecute()方法执行。在这个方法中你能看到我们填充了一个命名参数location,它来自于recentForLocation()方法的参数。

现在是时候阐明一些情况了。HibernateDaoSupportHibernateTemplate是来自于Spring Framework的类。它们由Spring Framework创建,目的是减少编写Hibernate DAO对象的痛苦。为了支持这个DAO,我们需要在simple-persist的Spring ApplicationContext定义中做一些配置。Example 7.10, “simple-persist 的 Spring Application Context”中显示的XML文档存储在src/main/resources,名为applicationContext-persist.xml

Example 7.10. simple-persist 的 Spring Application Context

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
             http://www.springframework.org/schema/beans/spring-beans-2.0.xsd"
    default-lazy-init="true">

    <bean id="sessionFactory"
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
        <property name="annotatedClasses">
            <list>
                <value>org.sonatype.mavenbook.weather.model.Atmosphere</value>
                <value>org.sonatype.mavenbook.weather.model.Condition</value>
                <value>org.sonatype.mavenbook.weather.model.Location</value>
                <value>org.sonatype.mavenbook.weather.model.Weather</value>
                <value>org.sonatype.mavenbook.weather.model.Wind</value>
            </list>
        </property>
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.show_sql">false</prop>
                <prop key="hibernate.format_sql">true</prop>
                <prop key="hibernate.transaction.factory_class">
                  org.hibernate.transaction.JDBCTransactionFactory
                </prop>
                <prop key="hibernate.dialect">
                  org.hibernate.dialect.HSQLDialect
                </prop>
                <prop key="hibernate.connection.pool_size">0</prop>
                <prop key="hibernate.connection.driver_class">
                  org.hsqldb.jdbcDriver
                </prop>
                <prop key="hibernate.connection.url">
                  jdbc:hsqldb:data/weather;shutdown=true
                </prop>
                <prop key="hibernate.connection.username">sa</prop>
                <prop key="hibernate.connection.password"></prop>
                <prop key="hibernate.connection.autocommit">true</prop>
                <prop key="hibernate.jdbc.batch_size">0</prop>
            </props>
        </property>
    </bean>

    <bean id="locationDAO" 
             class="org.sonatype.mavenbook.weather.persist.LocationDAO">
        <property name="sessionFactory" ref="sessionFactory"/>
    </bean>
    
    <bean id="weatherDAO" 
             class="org.sonatype.mavenbook.weather.persist.WeatherDAO">
        <property name="sessionFactory" ref="sessionFactory"/>
    </bean>
 </beans>

在这个application context中,我们完成了一些事情。DAOsessionFactory bean获取Hibernate Session对象。这个bean是一个AnnotationSessionFactoryBean的实例,并由一列annotatedClasses填充。注意这列标注类就是定义在我们simple-model模块中的那些类。接下来,sessionFactory通过一组Hibernate配置属性(hibernateProperties)配置。该例中,我们的Hibernate属性定义了许多设置:

hibernate.dialect

该设置控制如何生成数据库的SQL。由于我们正在使用HSQLDB数据库,我们的数据库方言设置成org.hibernate.dialect.HSQLDialect。Hibernate有所有主流数据库的方言,如Oracle,MySQL,Postgres和SQL Server。

hibernate.connection.*

该例中,我们从Spring配置中配置JDBC连接属性。我们的应用被配置成运行在./data/weather目录下的HSQLDB上。在实际的企业应用中,你更可能会使用如JNDI的东西以从你的应用程序代码中抽出数据库配置。

最后,在这个bean定义文件中,两个simple-persist DAO对象被创建并给予了对于刚定义的sessionFactory bean的引用。就像simple-weather中的Spring application context,这个applicationContext-persist.xml文件定义了一个大型企业应用设计中一个子模块的架构。如果你曾经从事过大量持久化类的集合相关的工作,你可能会发现,这些与你应用程序独立的application context文件,能帮助你快速的理解所有类之间的关系。

simple-persist中还有最后一块不清楚的地方。本章后面,我们将看到如何使用Maven Hibernate3插件,根据标注的模型对象来生成数据库schema。为了使它正确工作,Maven Hibernate3插件需要读取JDBC连接配置参数,那一列标注的类,以及src/main/resources中名为hibernate.cfg.xml文件的Hibernate配置。该文件(它重复了一些applicationContext-persist.xml中的配置)的目的是能让Maven Hibernate3插件能仅仅依靠标注就能生成数据定义语言(DDL)。如Example 7.11, “simple-persist 的 hibernate.cfg.xml”

Example 7.11. simple-persist 的 hibernate.cfg.xml

<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
        "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
        
<hibernate-configuration>
  <session-factory>
        
    <!-- SQL dialect -->
    <property name="dialect">org.hibernate.dialect.HSQLDialect</property>
    
    <!-- Database connection settings -->
    <property name="connection.driver_class">org.hsqldb.jdbcDriver</property>
    <property name="connection.url">jdbc:hsqldb:data/weather</property>
    <property name="connection.username">sa</property>
    <property name="connection.password"></property>
    <property name="connection.shutdown">true</property>
    
    <!-- JDBC connection pool (use the built-in one) -->
    <property name="connection.pool_size">1</property>
    
    <!-- Enable Hibernate's automatic session context management -->
    <property name="current_session_context_class">thread</property>
    
    <!-- Disable the second-level cache  -->
    <property name="cache.provider_class">
      org.hibernate.cache.NoCacheProvider
    </property>
    
    <!-- Echo all executed SQL to stdout -->
    <property name="show_sql">true</property>
    
    <!-- disable batching so HSQLDB will propagate errors correctly. -->
    <property name="jdbc.batch_size">0</property>
    
    <!-- List all the mapping documents we're using -->
    <mapping class="org.sonatype.mavenbook.weather.model.Atmosphere"/>
    <mapping class="org.sonatype.mavenbook.weather.model.Condition"/>
    <mapping class="org.sonatype.mavenbook.weather.model.Location"/>
    <mapping class="org.sonatype.mavenbook.weather.model.Weather"/>
    <mapping class="org.sonatype.mavenbook.weather.model.Wind"/>
        
  </session-factory>
</hibernate-configuration>

Example 7.10, “simple-persist 的 Spring Application Context”Example 7.1, “simple-parent 项目的 POM”的内容是冗余的。Spring Application Context XML是被web应用和命令行应用使用的,而hibernate.cfg.xml的存在只是为了支持Maven Hibernate3插件。本章的后面,我们将会看到如何使用hibernate.cfg.xml和Maven Hibernate3插件,根据simple-model中定义的标注对象模型,来生成一个数据库schema。hibernate.cfg.xml会配置JDBC连接属性并且为Maven Hibernate3插件枚举标注模型类的列表。