在AppFuse 2.0中使用Hibernate来写DAO

英文版网址 http://appfuse.org/display/APF/Using+Hibernate
 
关于本节教程
本教程主要关于以下两件事 :
1.      如果仅需要生成 CRUD 方法,那么不需要书写 DAOs
2.      需要定制 CRUD 方法时,应如何写 DAOs.
如果对 Hibernate 不太熟悉 , 也许在开始本课程之前需要读读 Hibernate Reference Guide.
目录
4.      运行 DAO 测试
关于源码
本课程的代码位置在 Google code appfuse-demos 项目里的 "tutorial-hibernate" 模块里 . 使用以下命令从 Subversion 服务器中检出 :
svn checkout http://appfuse-demos.googlecode.com/svn/trunk/tutorial-hibernate
注册personDao bean 定义
AppFuse 2.x 不需要写 DAO 来持久化 POJO. 如果你所需要的仅是对一个对象执行 CRUD 操作,那就可以使用以下预置的类之一 :
  • GenericDaoHibernate: 是一个通用的类,需要建立Spring bean 定义.
  • UniversalDaoHibernate: 一个特殊的类,需要建立指定的对象类型.
UniversalDaoHibernate类已经被注册为一个"dao" bean,所以不必增加附加的配置就可以使用. 然而,许多开发者更喜欢通用的DAOgenerics-based DAO),因为它提供了类型安全保证。为注册personDao bean,需要建立文件src/main/webapp/WEB-INF/applicationContext.xml (多模块架构是core/src/main/resources/applicationContext.xml)并添加以下内容:
注意,若使用 AppFuse 2.0 M5+ applicationContext.xml 文件可能已经存在了 .
<?xml version="1.0" encoding="UTF-8"?>
<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">
 
    <bean id="personDao" class="org.appfuse.dao.hibernate.GenericDaoHibernate">
        <constructor-arg value="org.appfuse.tutorial.model.Person"/>
        <property name="sessionFactory" ref="sessionFactory"/>
    </bean> 
</beans>
完成后,使用 Spring 的应用,再添加了以下 setter 方法后,就可以在一个对象上使用这个 bean :
public void setPersonDao(GenericDao<Person, Long> personDao) {
    this.personDao = personDao;
}
如果不仅仅需要CRUD 功能,那还要继续向下阅读本教程,否则,可以转而阅读Creating new Managers,这篇教程教授如何建立商业逻辑面,与Session Facades类似,但是不使用EJB技术。这些面(facades)用于提供前端与DAO层的通讯.
建立DAO 测试类测试finder方法
现在需要建立一个 DaoTest 类来测试 DAO 是否能工作。 " 请等一会 ," 你也许会说 ," 我还没创建 DAO!" 当然,你是正确的。然而,我发现测试驱动开发( Test-Driven Development )更有利于提高软件开发质量 . 多年来,我一直认为写类前先写测试毫无是处,而且看起来好像是很愚蠢的。但在尝试过以后,我发现这种开发方法实际上是很了不起的。现在,我使用测试驱动的唯一原因就是因为它能迅速提高软件开发效率 .
首先 , src/test/java/**/dao directory ( 多模原型下 core/src/test/java/**/dao ) 目录下建立类 PersonDaoTest.java,它要继承org.appfuse.dao.BaseDaoTestCase –是Spring AbstractTransactionalDataSourceSpringContextTests 的一个子类. 其父类是用于装载SpringApplicationContext (因为Spring 需要依据它来把接口( interfaces)绑定到实现上(implementations), 并且(可选的)装入一个与你的*Test同名的.properties 文件.在本例中,如果将一个命名为PersonDaoTest.properties的文件放入目录src/test/resources/org/appfuse/tutorial/dao, 那么文件中的属性将可以使用变量"rb"来取得.
package org.appfuse.tutorial.dao;
import java.util.List;
import org.appfuse.dao.BaseDaoTestCase;
import org.appfuse.tutorial.model.Person;
import org.springframework.dao.DataAccessException;
public class PersonDaoTest extends BaseDaoTestCase {
    private PersonDao personDao = null;
    public void setPersonDao(PersonDao personDao) {
        this.personDao = personDao;
    }
}
上述代码就是一个基本的与 Spring 整合的测试,初始化,并配置了 PersonDao 的实现 . Spring 将通过名字来自动回调 setPersonDao()方法,并建立与"personDao" bean的关联.
现在可以测试 DAO 中的 finder 方法了。首先建立一个以 "test" ( 全部小写 ) 开头的测试类 . ,访问权限是 public, 返回类型为 void,没有参数,它将被Junit调用执行,然后将以下方法加入PersonDaoTest.java文件:
public void testFindPersonByLastName() throws Exception {
    List<Person> people = personDao.findByLastName("Raible");
    assertTrue(people.size() > 0);
}
你会注意到,这个方法要测试通过必须要依赖一些预置的数据, DbUnit Maven Plugin 可以用来在测试运行前将数据插入数据库中, 你只需将需加入的表或记录的信息放到文件src/test/resources/sample-data.xml (多模原型是在 core/src/test/resources/sample-data.xml) ,以下为示例 :
<table name='person'>
 <column>id</column>
 <column>first_name</column>
 <column>last_name</column>
 <row>
    <value>1</value>
    <value>Matt</value>
    <value>Raible</value>
 </row>
</table>
如果重新格式 sample-data.xml 中的 XML, 要确保在标签( tag )值内没有换行符( line breaks . 但令我吃惊的是当 <password>xxxxxxx</password> 被分为三行的情景, 由于 password 标签不再对额外的 tabs 和换行符进行解密 , 现在想用任意用户名登录将不再可能。而允许在数据库连接串里使用任意字符是 DBUnit 的特性 .
由于将要写的 PersonDao 包含了 CRUD 功能,所以也要写个校验 CRUD 的测试 .
import org.springframework.dao.DataAccessException;
public void testAddAndRemovePerson() throws Exception {
    Person person = new Person();
    person.setFirstName("Country");
    person.setLastName("Bry");
    person = personDao.save(person);
    flush();
 
    person = personDao.get(person.getId()); 
    assertEquals("Country", person.getFirstName());
    assertNotNull(person.getId()); 
    log.debug("removing person..."); 
    personDao.remove(person.getId());
    flush();
 
    try {
        personDao.get(person.getId());
        fail("Person found in database");
    } catch (DataAccessException dae) {
        log.debug("Expected exception: " + dae.getMessage());
        assertNotNull(dae);
    }
}
上例中,在保存对象前,要先调用 person.set*(value) 方法设置对象,然后再保存起来。本例情况较简单,如果对象的属性超过了10个,那就麻烦了。这就是为什么在BaseDaoTestCase要有一个资源集合(ResourceBundle)的原因.首先在PersonDaoTest.java所在目录里简单地建立一个PersonDaoTest.properties文件,并定义属性值对如下:
firstName=Matt
lastName=Raible
也许觉得可以用硬编码来在程序里设置测试值,但是在处理大的对象时, .properties 文件是一个很好的选择 .
然后,不必调用 person.set* 来设置对象属性,只需使用 BaseDaoTestCase.populate(java.lang.Object)方法即可完成:
Person person = new Person();
person = (Person) populate(person);
此时, PersonDaoTest类还不能编译,因为还没有PersonDao 类,现在需要建立它了.
建立DAO 接口及其实现
src/main/java/**/dao ( 多模原型是 core/src/main/java/**/dao ) 目录里建立接口文件 PersonDao.java,并为所有的实现类指定finder方法.
package org.appfuse.tutorial.dao;
import org.appfuse.dao.GenericDao;
import org.appfuse.tutorial.model.Person; 
import java.util.List; 
public interface PersonDao extends GenericDao<Person, Long> {
    public List<Person> findByLastName(String lastName);
}
注意,上面这个类的方法声明( method signature )中并没有异常声明,这主要是归功于 Spring                       它使用 RuntimeExceptions 包装了异常( Exception . 现在,你可以使用 IDE 或命令 mvn test-compile 来编译代码了 . 然而,如果你试图运行 mvn test -Dtest=PersonDaoTest, 就会报错 :
Running org.appfuse.tutorial.dao.PersonDaoTest
INFO - AbstractSingleSpringContextTests.loadContextLocations(179) | Loading context for: classpath*:/applicationContext-*.xml
Tests run: 2, Failures: 0, Errors: 2, Skipped: 0, Time elapsed: 0.449 sec <<< FAILURE!
很不幸,这些信息并没有多少关于是导致报错的原因的,要找到问题所在 , 需要打开文件 target/surefire-reports/org.appfuse.tutorial.dao.PersonDaoTest.txt ( 在多模原型下是 core/target/surefire-reports/org.appfuse.tutorial.dao.PersonDaoTest.txt) ,在这里会告诉你有真正的问题所在 :
---------------------------------------------------------------------
Test set: org.appfuse.tutorial.dao.PersonDaoTest
---------------------------------------------------------------------
Tests run: 2, Failures: 0, Errors: 2, Skipped: 0, Time elapsed: 0.444 sec <<< FAILURE!
testFindPersonByLastName(org.appfuse.tutorial.dao.PersonDaoTest) Time elapsed: 0.401 sec <<< ERROR!
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean
with name 'org.appfuse.tutorial.dao.PersonDaoTest': Unsatisfied dependency expressed
through bean property 'personDao': Set this property value or disable dependency
checking for this bean.
在控制台(控制窗口)中显示错误
mvn test 命令里添加参数 -Dsurefire.useFile=false 就可以在控制台( console )中现实测试错误信息。
这个错误信息来自 Spring – 指出需要在 Spring context 文件里指定一个命名为 "personDao" bean. 在做这件事之前,首先需要建立 PersonDao的实现类.
我们建立一个 PersonDaoHibernate类来实现PersonDao里的finder 方法.首先,在src/main/java/**/dao/hibernate (多模原型 core/src/main/java/**/dao/hibernate)目录下建立一个类文件,并命名为PersonDaoHibernate.java. 需要继承GenericDaoHibernate并且实现PersonDao。一个简短的例子如下:
package org.appfuse.tutorial.dao.hibernate;
import java.util.List; 
import org.appfuse.dao.hibernate.GenericDaoHibernate;
import org.appfuse.tutorial.model.Person;
import org.appfuse.tutorial.dao.PersonDao; 
public class PersonDaoHibernate extends GenericDaoHibernate<Person, Long> implements PersonDao { 
    public PersonDaoHibernate() {
        super(Person.class);
    } 
    public List<Person> findByLastName(String lastName) {
        return getHibernateTemplate().find("from Person where lastName=?", lastName);
    }
}
现在如果再运行 mvn test -Dtest=PersonDaoTest , 还会看到同样的错误,因为还要配置 Spring 告诉它 PersonDaoHibernatePersonDao.的实现类。
src/main/webapp/WEB-INF (or core/src/main/resources for a modular archetype) 目录下建立文件 applicationContext.xml,并添加以下内容分:
<?xml version="1.0" encoding="UTF-8"?>
<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">
 
    <bean id="personDao" class="org.appfuse.tutorial.dao.hibernate.PersonDaoHibernate">
        <property name="sessionFactory" ref="sessionFactory"/>
    </bean> 
</beans>
运行DAO 测试
保存编辑的文件,运行命令 mvn test -Dtest=PersonDaoTest ,过一会后,
是的,朋友,就是这条信息 :
BUILD SUCCESSFUL
Total time: 9 seconds
在视图过滤器( View Filter )中打开会话( Session
如果希望在应用中使用 Hibernate 的延迟载入( lazy-loading )特性, 那么,需要在文件 web.xml 中去掉 lazyLoadingFilter ( 及其 mapping) 的注释 .

接下来: Part II: 建立管理类( Creating new Managers - 关于如何建立商业逻辑层( Business Facades , 与会话层( Session Facades )类似 , 但是不使用 EJB. 这些层用于提供前台( front-end )与 DAO 层的通讯。
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值