英文版网址
:
http://appfuse.org/display/APF/Using+Hibernate
关于本节教程
本教程主要关于以下两件事
:
1.
如果仅需要生成
CRUD
方法,那么不需要书写
DAOs
2.
需要定制
CRUD
方法时,应如何写
DAOs.
目录
4.
运行 DAO 测试
关于源码
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,所以不必增加附加的配置就可以使用. 然而,许多开发者更喜欢通用的DAO(generics-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 的一个子类. 其父类是用于装载Spring的ApplicationContext (因为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
告诉它
PersonDaoHibernate是PersonDao.的实现类。
在
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
Total time: 9 seconds
在视图过滤器(
View Filter
)中打开会话(
Session
)
如果希望在应用中使用 Hibernate 的延迟载入( lazy-loading )特性, 那么,需要在文件 web.xml 中去掉 lazyLoadingFilter ( 及其 mapping) 的注释 . |