之前的博文中我们搭建了一个基本的开发环境,现在让我们给这个项目加上CRUD和基本的单元测试。
1、创建泛型DAO的相关类
在src\main\java\com\ceair\app\dao下创建GenericDao.java(APPFUSE2中的代码),代码内容如下:
- package com.ceair.app.dao;
-
- import java.io.Serializable;
- import java.util.List;
- import java.util.Map;
-
-
-
-
-
-
-
-
-
-
-
-
- public interface GenericDao <T, PK extends Serializable> {
-
-
-
-
-
-
- List<T> getAll();
-
-
-
-
-
-
-
-
-
-
- T get(PK id);
-
-
-
-
-
-
- boolean exists(PK id);
-
-
-
-
-
-
- T save(T object);
-
-
-
-
-
- void remove(PK id);
-
-
-
-
-
-
-
- List<T> getAllDistinct();
-
-
-
-
-
-
-
-
- List<T> findByNamedQuery(String queryName, Map<String, Object> queryParams);
- }
在src\main\java\com\ceair\app\dao\hibernate中创建GenericDaoHibernate.java实现类,代码如下:
- package com.ceair.app.dao.hibernate;
-
-
- import org.apache.commons.logging.Log;
- import org.apache.commons.logging.LogFactory;
- import org.springframework.orm.ObjectRetrievalFailureException;
- import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
-
- import com.ceair.app.dao.GenericDao;
-
- import java.io.Serializable;
- import java.util.ArrayList;
- import java.util.Collection;
- import java.util.Iterator;
- import java.util.LinkedHashSet;
- import java.util.List;
- import java.util.Map;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- public class GenericDaoHibernate<T, PK extends Serializable> extends HibernateDaoSupport implements GenericDao<T, PK> {
-
-
-
- protected final Log log = LogFactory.getLog(getClass());
- private Class<T> persistentClass;
-
-
-
-
-
- public GenericDaoHibernate(final Class<T> persistentClass) {
- this.persistentClass = persistentClass;
- }
-
-
-
-
- @SuppressWarnings("unchecked")
- public List<T> getAll() {
- return super.getHibernateTemplate().loadAll(this.persistentClass);
- }
-
-
-
-
- @SuppressWarnings("unchecked")
- public List<T> getAllDistinct() {
- Collection result = new LinkedHashSet(getAll());
- return new ArrayList(result);
- }
-
-
-
-
- @SuppressWarnings("unchecked")
- public T get(PK id) {
- T entity = (T) super.getHibernateTemplate().get(this.persistentClass, id);
-
- if (entity == null) {
- log.warn("Uh oh, '" + this.persistentClass + "' object with id '" + id + "' not found...");
- throw new ObjectRetrievalFailureException(this.persistentClass, id);
- }
-
- return entity;
- }
-
-
-
-
- @SuppressWarnings("unchecked")
- public boolean exists(PK id) {
- T entity = (T) super.getHibernateTemplate().get(this.persistentClass, id);
- return entity != null;
- }
-
-
-
-
- @SuppressWarnings("unchecked")
- public T save(T object) {
- return (T) super.getHibernateTemplate().merge(object);
- }
-
-
-
-
- public void remove(PK id) {
- super.getHibernateTemplate().delete(this.get(id));
- }
-
-
-
-
- @SuppressWarnings("unchecked")
- public List<T> findByNamedQuery(
- String queryName,
- Map<String, Object> queryParams) {
- String []params = new String[queryParams.size()];
- Object []values = new Object[queryParams.size()];
- int index = 0;
- Iterator<String> i = queryParams.keySet().iterator();
- while (i.hasNext()) {
- String key = i.next();
- params[index] = key;
- values[index++] = queryParams.get(key);
- }
- return getHibernateTemplate().findByNamedQueryAndNamedParam(
- queryName,
- params,
- values);
- }
- }
在src\test\java\com\ceair\app\dao下创建单元测试基类BaseDaoTestCase.java,代码如下:
- package com.ceair.app.dao;
-
- import org.apache.commons.beanutils.BeanUtils;
- import org.apache.commons.logging.Log;
- import org.apache.commons.logging.LogFactory;
- import org.hibernate.SessionFactory;
- import org.springframework.orm.hibernate3.HibernateTemplate;
- import org.springframework.test.AbstractTransactionalDataSourceSpringContextTests;
-
- import java.util.Enumeration;
- import java.util.HashMap;
- import java.util.Map;
- import java.util.MissingResourceException;
- import java.util.ResourceBundle;
-
-
-
-
-
- public abstract class BaseDaoTestCase extends AbstractTransactionalDataSourceSpringContextTests {
-
-
-
- protected final Log log = LogFactory.getLog(getClass());
-
-
-
- protected ResourceBundle rb;
-
-
-
-
-
- protected String[] getConfigLocations() {
- setAutowireMode(AUTOWIRE_BY_NAME);
- return new String[] {
-
- "classpath:/applicationContext-dao.xml",
- "classpath*:/applicationContext.xml",
- "classpath:/applicationContext-hibernate.xml"
-
- };
- }
-
-
-
-
-
- public BaseDaoTestCase() {
-
-
- String className = this.getClass().getName();
-
- try {
- rb = ResourceBundle.getBundle(className);
- } catch (MissingResourceException mre) {
-
- }
-
- }
-
-
-
-
-
-
-
-
- protected Object populate(Object obj) throws Exception {
-
- Map<String, String> map = new HashMap<String, String>();
-
- for (Enumeration<String> keys = rb.getKeys(); keys.hasMoreElements();) {
- String key = keys.nextElement();
- map.put(key, rb.getString(key));
- }
-
- BeanUtils.copyProperties(map, obj);
-
- return obj;
- }
-
-
-
-
-
- protected void flush() {
- HibernateTemplate hibernateTemplate =
- new HibernateTemplate((SessionFactory) applicationContext.getBean("sessionFactory"));
- hibernateTemplate.flush();
- hibernateTemplate.clear();
-
- }
- }
这个类的注意点如下:
1、38-40行( classpath:/applicationContext-dao.xml等)是指向你的配置文件,等下我会列出这些配置文件的详细信息。
2、去掉59行那句代码( super.setDefaultRollback(false); )注释的话,如果有数据库操作,那么不回滚测试数据。
在src\main\resources下分别创建applicationContext.xml,applicationContext-dao.xml,applicationContext-hibernate.xml 我们把applicationContext.xml拆解成3个文件,有助于明确配置的作用。
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"
- xmlns:context="http://www.springframework.org/schema/context"
-
- xsi:schemaLocation="http:
- http:
- http:
- <!-- Example of SAF2 action instantiated by Spring -->
- <bean id="helloWorldAction" class="com.ceair.app.HelloWorldAction" />
- <bean id="userAction" class="com.ceair.app.action.UserAction"/>
- <context:annotation-config/>
- <context:component-scan base-package="com.ceair"/>
-
- </beans>
注意点: 1、12-13行(<context:annotation-config/> 和<context:component-scan base-
package
=
"com.ceair"
/> )的加入是因为在只有的单元测试类中要用到一些注解,如@Autowire
2、因为12-13行的加入,所以要增加第4行(xmlns:context=
"http://www.springframework.org/schema/context")和第8行(http:
)的声明。
applicationContext-dao.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"
- xmlns:jee="http://www.springframework.org/schema/jee"
- xsi:schemaLocation="http:
- http:
- <bean id="userDao" class="com.ceair.app.dao.hibernate.GenericDaoHibernate">
- <constructor-arg value="com.ceair.app.model.User"/>
- <property name="sessionFactory" ref="sessionFactory"/>
- </bean>
-
-
- </beans>
applicationContext-hibernate.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"
- xmlns:jee="http://www.springframework.org/schema/jee"
- xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
- http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.0.xsd">
-
- <bean id="propertyConfigurer"
- class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
- <property name="locations">
- <list>
- <value>classpath:jdbc.properties</value>
- </list>
- </property>
- </bean>
- <!--
- DriverManagerDataSource不具备连接池的功能 <bean id="dataSource"
- class="org.springframework.jdbc.datasource.DriverManagerDataSource"
- destroy-method="close"> <property name="driverClassName"
- value="${hibernate.connection.driver_class}"/> <property name="url"
- value="${hibernate.connection.url}"/> <property name="username"
- value="${hibernate.connection.username}"/> <property name="password"
- value="${hibernate.connection.password}"/> <property name="maxActive"
- value="100"/> <property name="maxWait" value="1000"/> <property
- name="poolPreparedStatements" value="true"/> <property
- name="defaultAutoCommit" value="true"/> </bean>
- -->
- <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
- destroy-method="close">
- <property name="driverClass">
- <value>${hibernate.connection.driver_class}</value>
- </property>
- <property name="jdbcUrl">
- <value>${hibernate.connection.url}</value>
- </property>
- <property name="user">
- <value>${hibernate.connection.username}</value>
- </property>
- <property name="password">
- <value>${hibernate.connection.password}</value>
- </property>
-
-
- <property name="minPoolSize">
- <value>5</value>
- </property>
-
-
- <property name="maxPoolSize">
- <value>30</value>
- </property>
-
-
- <property name="initialPoolSize">
- <value>10</value>
- </property>
-
-
- <property name="maxIdleTime">
- <value>60</value>
- </property>
-
-
- <property name="acquireIncrement">
- <value>5</value>
- </property>
-
- <!--
- JDBC的标准参数,用以控制数据源内加载的PreparedStatements数量。但由于预缓存的statements
- 属于单个connection而不是整个连接池。所以设置这个参数需要考虑到多方面的因素。
- 如果maxStatements与maxStatementsPerConnection均为0,则缓存被关闭。Default: 0
- -->
- <property name="maxStatements">
- <value>150</value>
- </property>
-
-
- <property name="idleConnectionTestPeriod">
- <value>60</value>
- </property>
-
-
- <property name="acquireRetryAttempts">
- <value>30</value>
- </property>
-
- <!--
- 获取连接失败将会引起所有等待连接池来获取连接的线程抛出异常。但是数据源仍有效
- 保留,并在下次调用getConnection()的时候继续尝试获取连接。如果设为true,那么在尝试
- 获取连接失败后该数据源将申明已断开并永久关闭。Default: false
- -->
- <property name="breakAfterAcquireFailure">
- <value>true</value>
- </property>
-
- <!--
- 因性能消耗大请只在需要的时候使用它。如果设为true那么在每个connection提交的
- 时候都将校验其有效性。建议使用idleConnectionTestPeriod或automaticTestTable
- 等方法来提升连接测试的性能。Default: false
- -->
- <property name="testConnectionOnCheckout">
- <value>false</value>
- </property>
- </bean>
- <bean id="sessionFactory"
- class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
- <property name="dataSource" ref="dataSource" />
- <property name="configLocation" value="classpath:hibernate.cfg.xml" />
-
- <property name="hibernateProperties">
- <props>
- <prop key="hibernate.dialect">${hibernate.dialect}</prop>
- <prop key="hibernate.cache.use_second_level_cache">false</prop>
- <prop key="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</prop>
- <prop key="show_sql">true</prop>
- </props>
- </property>
- </bean>
- <bean id="transactionManager"
- class="org.springframework.orm.hibernate3.HibernateTransactionManager">
- <property name="sessionFactory" ref="sessionFactory" />
- </bean>
- </beans>
2、创建单元测试类
在src\test\java\com\ceair\app创建UserDaoTest.java,代码如下:
- package com.ceair.app;
-
-
-
-
- import org.junit.Test;
- import org.springframework.beans.factory.annotation.Autowired;
-
- import com.ceair.app.dao.BaseDaoTestCase;
- import com.ceair.app.dao.GenericDao;
- import com.ceair.app.model.User;
-
- public class UserDaoTest extends BaseDaoTestCase {
- @Autowired
- private GenericDao<User, Long> userDao;
-
-
-
- @Test
- public void testUserById() throws Exception {
- User people = userDao.get(new Long(-2));
- assertEquals("管理员", people.getName());
- }
-
- @Test
- public void testAddAndRemovePerson() throws Exception {
- User person = new User();
- person.setId(new Long(3));
- person.setName("陈小林");
- person = userDao.save(person);
- flush();
-
- person = userDao.get(person.getId());
-
- assertEquals("陈小林", person.getName());
- assertNotNull(person.getId());
- person.setName("陈小");
-
- flush();
-
- person = userDao.get(person.getId());
- assertEquals("陈小", person.getName());
- assertNotNull(person.getId());
-
-
- }
-
- }
注意:1、测试数据有的来自于sample-data.xml如21行(User people = userDao.get(
new
Long(-
2
)); )主键为-2的对象。
以上工作全部完成后,打开命令行进入项目目录运行mvn test就可以运行单元测试。
如果我们除了CRUD功能还需要其它方法呢?我们接下去将演示自定义接口和声明式事务
1、在src\main\java\com\ceair\app\dao下创建UserDao.java,代码如下:
- package com.ceair.app.dao;
-
- import java.util.List;
-
- import org.hibernate.criterion.DetachedCriteria;
-
- import com.ceair.app.model.User;
-
- public interface UserDao extends GenericDao<User, Long> {
- public List<User> findByCrteria(DetachedCriteria crteria);
-
- public void changeName(String id,String newName) throws Exception;
- }
2、在src\main\java\com\ceair\app\dao\hibernate下创建UserDaoHibernate.java,代码如下:
- package com.ceair.app.dao.hibernate;
-
- import java.util.List;
-
- import org.hibernate.criterion.DetachedCriteria;
- import org.hibernate.criterion.Restrictions;
- import org.springframework.transaction.annotation.Isolation;
- import org.springframework.transaction.annotation.Propagation;
- import org.springframework.transaction.annotation.Transactional;
-
- import com.ceair.app.dao.UserDao;
- import com.ceair.app.model.User;
- public class UserDaoHibernate extends GenericDaoHibernate<User, Long> implements UserDao {
-
- public UserDaoHibernate() {
- super(User.class);
- }
-
- @SuppressWarnings("unchecked")
-
- public List<User> findByCrteria(DetachedCriteria crteria) {
- return getHibernateTemplate().findByCriteria(crteria);
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- @Transactional(
-
- propagation = Propagation.REQUIRED,
-
- isolation = Isolation.DEFAULT,
-
- rollbackFor = Exception.class
-
- )
- public void changeName(String id, String newName) throws Exception {
- DetachedCriteria crteria = DetachedCriteria.forClass(User.class);
- crteria.add(Restrictions.eq("id", new Long(id)));
- List<User> userList = getHibernateTemplate().findByCriteria(crteria);
- User user = userList.get(0);
- user.setName(newName);
- getHibernateTemplate().update(user);
-
- }
-
- }
-
注意:1、去掉55行( throw new Exception();//rollback
)的注释,会回滚数据库操作。
3、修改\src\main\resources下的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"
- xmlns:context="http://www.springframework.org/schema/context"
- xmlns:tx="http://www.springframework.org/schema/tx"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
- http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
- http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">
-
- <bean id="helloWorldAction" class="com.ceair.app.HelloWorldAction" />
- <bean id="userAction" class="com.ceair.app.action.UserAction"/>
- <context:annotation-config/>
- <context:component-scan base-package="com.ceair"/>
- <tx:annotation-driven transaction-manager="transactionManager"/>
- </beans>
-
注意:1、增加了13行(
<context:annotation-config/>
)声明(支持SPRING事务标注),增加第5(
xmlns:tx
=
"http://www.springframework.org/schema/tx"),第8( http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd )行的命名空间声明。
4、修改\src\main\resources下的applicationContext-dao.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"
- xmlns:jee="http://www.springframework.org/schema/jee"
- xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
- http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.0.xsd">
-
- <bean id="userCustomDao" class="com.ceair.app.dao.hibernate.UserDaoHibernate" >
- <property name="sessionFactory" ref="sessionFactory"/>
- </bean>
- </beans>
注意:1、增加7-8行,把原来的Bean userDao删除了
5、修改src\test\java\com\ceair\app下的UserDaoTest.java,修改后内容如下:
- package com.ceair.app;
-
-
-
-
- import org.junit.Test;
- import org.springframework.beans.factory.annotation.Autowired;
-
- import com.ceair.app.dao.BaseDaoTestCase;
- import com.ceair.app.dao.UserDao;
- import com.ceair.app.model.User;
-
- public class UserDaoTest extends BaseDaoTestCase {
- @Autowired
- private UserDao userCustomDao;
-
-
- @Test
- public void testUserById() throws Exception {
- User people = userCustomDao.get(new Long(-2));
- assertEquals("管理员", people.getName());
- }
- @Test
- public void testAddAndRemovePerson() throws Exception {
- User person = new User();
- person.setId(new Long(3));
- person.setName("陈小林");
- person = userCustomDao.save(person);
- flush();
-
- person = userCustomDao.get(person.getId());
-
- assertEquals("陈小林", person.getName());
- assertNotNull(person.getId());
- person.setName("陈小");
-
- flush();
-
- person = userCustomDao.get(person.getId());
- assertEquals("陈小", person.getName());
- assertNotNull(person.getId());
- log.debug("removing person...");
-
- }
- }
6、在src\test\java\com\ceair\app下创建UserCustomDaoTest.java,代码内容如下:
- package com.ceair.app;
-
-
-
-
- import java.util.List;
-
- import org.hibernate.criterion.DetachedCriteria;
- import org.hibernate.criterion.Restrictions;
- import org.junit.Test;
- import org.springframework.beans.factory.annotation.Autowired;
-
- import com.ceair.app.dao.BaseDaoTestCase;
- import com.ceair.app.dao.UserDao;
- import com.ceair.app.model.User;
-
- public class UserCustomDaoTest extends BaseDaoTestCase {
-
- @Autowired
- private UserDao userCustomDao;
-
- @Test
- public void testFindByCrteria() throws Exception {
- DetachedCriteria crteria=DetachedCriteria.forClass(User.class);
- crteria.add(Restrictions.eq("name", "管理员"));
- List<User> userList=userCustomDao.findByCrteria(crteria);
-
- assertEquals("管理员", userList.get(0).getName());
- assertEquals(new Long(-2), userList.get(0).getId());
- }
-
- @Test
- public void testChangeName() throws Exception {
- userCustomDao.changeName("-2", "新管理员");
- flush();
- User people = userCustomDao.get(new Long(-2));
- assertEquals("新管理员", people.getName());
- }
- }
注意:1、事务测试前可以把BaseDaoTestCase.java59行(不回滚测试产生的数据)和UserDaoHibernate .java55行(抛出异常,回滚事务)和的注释去除,然后去数据库中查看是否更改名字成功。
以上工作全部完成后,打开命令行进入项目目录运行mvn test就可以运行单元测试。
关于事务的深入研究可以查看 http://www.ibm.com/developerworks/cn/java/j-ts1.html