之前的博文中我们搭建了一个基本的开发环境,现在让我们给这个项目加上CRUD和基本的单元测试。
1、创建泛型DAO的相关类
在src\main\java\com\ceair\app\dao下创建GenericDao.java(APPFUSE2中的代码),代码内容如下:
 
  
  1. package com.ceair.app.dao;  
  2.  
  3. import java.io.Serializable;  
  4. import java.util.List;  
  5. import java.util.Map;  
  6.  
  7.  
  8. /**  
  9.  * Generic DAO (Data Access Object) with common methods to CRUD POJOs.  
  10.  *  
  11.  * <p>Extend this interface if you want typesafe (no casting necessary) DAO's for your  
  12.  * domain objects.  
  13.  *  
  14.  * @author <a href="mailto:bwnoll@gmail.com">Bryan Noll</a>  
  15.  * @param <T> a type variable  
  16.  * @param <PK> the primary key for that type  
  17.  */ 
  18. public interface GenericDao <T, PK extends Serializable> {  
  19.  
  20.     /**  
  21.      * Generic method used to get all objects of a particular type. This  
  22.      * is the same as lookup up all rows in a table.  
  23.      * @return List of populated objects  
  24.      */ 
  25.     List<T> getAll();  
  26.  
  27.     /**  
  28.      * Generic method to get an object based on class and identifier. An  
  29.      * ObjectRetrievalFailureException Runtime Exception is thrown if  
  30.      * nothing is found.  
  31.      *  
  32.      * @param id the identifier (primary key) of the object to get  
  33.      * @return a populated object  
  34.      * @see org.springframework.orm.ObjectRetrievalFailureException  
  35.      */ 
  36.     T get(PK id);  
  37.  
  38.     /**  
  39.      * Checks for existence of an object of type T using the id arg.  
  40.      * @param id the id of the entity  
  41.      * @return - true if it exists, false if it doesn't  
  42.      */ 
  43.     boolean exists(PK id);  
  44.  
  45.     /**  
  46.      * Generic method to save an object - handles both update and insert.  
  47.      * @param object the object to save  
  48.      * @return the persisted object  
  49.      */ 
  50.     T save(T object);  
  51.  
  52.     /**  
  53.      * Generic method to delete an object based on class and id  
  54.      * @param id the identifier (primary key) of the object to remove  
  55.      */ 
  56.     void remove(PK id);  
  57.       
  58.     /**  
  59.      * Gets all records without duplicates.  
  60.      * <p>Note that if you use this method, it is imperative that your model  
  61.      * classes correctly implement the hashcode/equals methods</p>  
  62.      * @return List of populated objects  
  63.      */ 
  64.     List<T> getAllDistinct();  
  65.       
  66.  
  67.     /**  
  68.      * Find a list of records by using a named query  
  69.      * @param queryName query name of the named query  
  70.      * @param queryParams a map of the query names and the values  
  71.      * @return a list of the records found  
  72.      */ 
  73.     List<T> findByNamedQuery(String queryName, Map<String, Object> queryParams);  
在src\main\java\com\ceair\app\dao\hibernate中创建GenericDaoHibernate.java实现类,代码如下:
 
  
  1. package com.ceair.app.dao.hibernate;  
  2.  
  3.  
  4. import org.apache.commons.logging.Log;  
  5. import org.apache.commons.logging.LogFactory;  
  6. import org.springframework.orm.ObjectRetrievalFailureException;  
  7. import org.springframework.orm.hibernate3.support.HibernateDaoSupport;  
  8.  
  9. import com.ceair.app.dao.GenericDao;  
  10.  
  11. import java.io.Serializable;  
  12. import java.util.ArrayList;  
  13. import java.util.Collection;  
  14. import java.util.Iterator;  
  15. import java.util.LinkedHashSet;  
  16. import java.util.List;  
  17. import java.util.Map;  
  18.  
  19. /**  
  20.  * This class serves as the Base class for all other DAOs - namely to hold  
  21.  * common CRUD methods that they might all use. You should only need to extend  
  22.  * this class when your require custom CRUD logic.  
  23.  *  
  24.  * <p>To register this class in your Spring context file, use the following XML.  
  25.  * <pre>  
  26.  *      &lt;bean id="fooDao" class="org.appfuse.dao.hibernate.GenericDaoHibernate"&gt;  
  27.  *          &lt;constructor-arg value="org.appfuse.model.Foo"/&gt;  
  28.  *          &lt;property name="sessionFactory" ref="sessionFactory"/&gt;  
  29.  *      &lt;/bean&gt;  
  30.  * </pre>  
  31.  *  
  32.  * @author <a href="mailto:bwnoll@gmail.com">Bryan Noll</a>  
  33.  * @param <T> a type variable  
  34.  * @param <PK> the primary key for that type  
  35.  */ 
  36. public class GenericDaoHibernate<T, PK extends Serializable> extends HibernateDaoSupport implements GenericDao<T, PK> {  
  37.     /**  
  38.      * Log variable for all child classes. Uses LogFactory.getLog(getClass()) from Commons Logging  
  39.      */ 
  40.     protected final Log log = LogFactory.getLog(getClass());  
  41.     private Class<T> persistentClass;  
  42.  
  43.     /**  
  44.      * Constructor that takes in a class to see which type of entity to persist  
  45.      * @param persistentClass the class type you'd like to persist  
  46.      */ 
  47.     public GenericDaoHibernate(final Class<T> persistentClass) {  
  48.         this.persistentClass = persistentClass;  
  49.     }  
  50.  
  51.     /**  
  52.      * {@inheritDoc}  
  53.      */ 
  54.     @SuppressWarnings("unchecked")  
  55.     public List<T> getAll() {  
  56.         return super.getHibernateTemplate().loadAll(this.persistentClass);  
  57.     }  
  58.       
  59.     /**  
  60.      * {@inheritDoc}  
  61.      */ 
  62.     @SuppressWarnings("unchecked")  
  63.     public List<T> getAllDistinct() {  
  64.         Collection result = new LinkedHashSet(getAll());  
  65.         return new ArrayList(result);  
  66.     }  
  67.       
  68.     /**  
  69.      * {@inheritDoc}  
  70.      */ 
  71.     @SuppressWarnings("unchecked")  
  72.     public T get(PK id) {  
  73.         T entity = (T) super.getHibernateTemplate().get(this.persistentClass, id);  
  74.  
  75.         if (entity == null) {  
  76.             log.warn("Uh oh, '" + this.persistentClass + "' object with id '" + id + "' not found...");  
  77.             throw new ObjectRetrievalFailureException(this.persistentClass, id);  
  78.         }  
  79.  
  80.         return entity;  
  81.     }  
  82.  
  83.     /**  
  84.      * {@inheritDoc}  
  85.      */ 
  86.     @SuppressWarnings("unchecked")  
  87.     public boolean exists(PK id) {  
  88.         T entity = (T) super.getHibernateTemplate().get(this.persistentClass, id);  
  89.         return entity != null;  
  90.     }  
  91.  
  92.     /**  
  93.      * {@inheritDoc}  
  94.      */ 
  95.     @SuppressWarnings("unchecked")  
  96.     public T save(T object) {  
  97.         return (T) super.getHibernateTemplate().merge(object);  
  98.     }  
  99.  
  100.     /**  
  101.      * {@inheritDoc}  
  102.      */ 
  103.     public void remove(PK id) {  
  104.         super.getHibernateTemplate().delete(this.get(id));  
  105.     }  
  106.       
  107.    /**   
  108.     * {@inheritDoc}  
  109.     */ 
  110.    @SuppressWarnings("unchecked")  
  111.    public List<T> findByNamedQuery(  
  112.        String queryName,   
  113.        Map<String, Object> queryParams) {  
  114.        String []params = new String[queryParams.size()];  
  115.        Object []values = new Object[queryParams.size()];  
  116.        int index = 0;  
  117.        Iterator<String> i = queryParams.keySet().iterator();  
  118.        while (i.hasNext()) {  
  119.            String key = i.next();  
  120.            params[index] = key;  
  121.            values[index++] = queryParams.get(key);  
  122.        }  
  123.        return getHibernateTemplate().findByNamedQueryAndNamedParam(  
  124.            queryName,   
  125.            params,   
  126.            values);  
  127.    }  
  128. }  
在src\test\java\com\ceair\app\dao下创建单元测试基类BaseDaoTestCase.java,代码如下:
 
  
  1. package com.ceair.app.dao;  
  2.  
  3. import org.apache.commons.beanutils.BeanUtils;  
  4. import org.apache.commons.logging.Log;  
  5. import org.apache.commons.logging.LogFactory;  
  6. import org.hibernate.SessionFactory;  
  7. import org.springframework.orm.hibernate3.HibernateTemplate;  
  8. import org.springframework.test.AbstractTransactionalDataSourceSpringContextTests;  
  9.  
  10. import java.util.Enumeration;  
  11. import java.util.HashMap;  
  12. import java.util.Map;  
  13. import java.util.MissingResourceException;  
  14. import java.util.ResourceBundle;  
  15.  
  16. /**  
  17.  * Base class for running DAO tests.  
  18.  * @author mraible  
  19.  */ 
  20. public abstract class BaseDaoTestCase extends AbstractTransactionalDataSourceSpringContextTests {  
  21.     /**  
  22.      * Log variable for all child classes. Uses LogFactory.getLog(getClass()) from Commons Logging  
  23.      */ 
  24.     protected final Log log = LogFactory.getLog(getClass());  
  25.     /**  
  26.      * ResourceBundle loaded from src/test/resources/${package.name}/ClassName.properties (if exists)  
  27.      */ 
  28.     protected ResourceBundle rb;  
  29.  
  30.     /**  
  31.      * Sets AutowireMode to AUTOWIRE_BY_NAME and configures all context files needed to tests DAOs.  
  32.      * @return String array of Spring context files.  
  33.      */ 
  34.     protected String[] getConfigLocations() {  
  35.         setAutowireMode(AUTOWIRE_BY_NAME);  
  36.         return new String[] {  
  37.              //  "classpath:/applicationContext-resources.xml",  
  38.                "classpath:/applicationContext-dao.xml",  
  39.                "classpath*:/applicationContext.xml"// for modular projects  
  40.                "classpath:/applicationContext-hibernate.xml"   
  41.             //    "classpath:**/applicationContext*.xml" // for web projects  
  42.             };  
  43.     }  
  44.  
  45.     /**  
  46.      * Default constructor - populates "rb" variable if properties file exists for the class in  
  47.      * src/test/resources.  
  48.      */ 
  49.     public BaseDaoTestCase() {  
  50.         // Since a ResourceBundle is not required for each class, just  
  51.         // do a simple check to see if one exists  
  52.         String className = this.getClass().getName();  
  53.  
  54.         try {  
  55.             rb = ResourceBundle.getBundle(className);  
  56.         } catch (MissingResourceException mre) {  
  57.             //log.warn("No resource bundle found for: " + className);  
  58.         }  
  59.       //  super.setDefaultRollback(false);  //不回滚数据  
  60.     }  
  61.  
  62.     /**  
  63.      * Utility method to populate a javabean-style object with values  
  64.      * from a Properties file  
  65.      * @param obj the model object to populate  
  66.      * @return Object populated object  
  67.      * @throws Exception if BeanUtils fails to copy properly  
  68.      */ 
  69.     protected Object populate(Object obj) throws Exception {  
  70.         // loop through all the beans methods and set its properties from its .properties file  
  71.         Map<String, String> map = new HashMap<String, String>();  
  72.  
  73.         for (Enumeration<String> keys = rb.getKeys(); keys.hasMoreElements();) {  
  74.             String key = keys.nextElement();  
  75.             map.put(key, rb.getString(key));  
  76.         }  
  77.  
  78.         BeanUtils.copyProperties(map, obj);  
  79.  
  80.         return obj;  
  81.     }  
  82.  
  83.     /**  
  84.      * Create a HibernateTemplate from the SessionFactory and call flush() and clear() on it.  
  85.      * Designed to be used after "save" methods in tests: http://issues.appfuse.org/browse/APF-178.  
  86.      */ 
  87.     protected void flush() {  
  88.         HibernateTemplate hibernateTemplate =  
  89.                 new HibernateTemplate((SessionFactory) applicationContext.getBean("sessionFactory"));  
  90.         hibernateTemplate.flush();  
  91.         hibernateTemplate.clear();  
  92.           
  93.     }  
  94. }  
这个类的注意点如下:
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 内容如下:
 
  
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <beans xmlns="http://www.springframework.org/schema/beans" 
  3.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  4.     xmlns:context="http://www.springframework.org/schema/context" 
  5.    
  6.     xsi:schemaLocation="http://www.springframework.org/schema/beans   
  7.            http://www.springframework.org/schema/beans/spring-beans-2.5.xsd  
  8.            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">  
  9.     <!-- Example of SAF2 action instantiated by Spring -->  
  10.     <bean id="helloWorldAction"  class="com.ceair.app.HelloWorldAction" />  
  11.      <bean id="userAction"  class="com.ceair.app.action.UserAction"/>    
  12.        <context:annotation-config/>    
  13.       <context:component-scan base-package="com.ceair"/>    
  14.      
  15. </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: //www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">   )的声明。
applicationContext-dao.xml内容如下:
 
 
  
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  3.        xmlns:jee="http://www.springframework.org/schema/jee" 
  4.        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd  
  5.             http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.0.xsd">  
  6.      <bean id="userDao" class="com.ceair.app.dao.hibernate.GenericDaoHibernate">  
  7.          <constructor-arg value="com.ceair.app.model.User"/>   
  8.          <property name="sessionFactory" ref="sessionFactory"/>  
  9.     </bean>  
  10.       
  11.       
  12. </beans> 
applicationContext-hibernate.xml 内容如下:
 
  
  1. <?xml version="1.0" encoding="UTF-8"?> 
  2. <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  3.        xmlns:jee="http://www.springframework.org/schema/jee" 
  4.        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd  
  5.             http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.0.xsd"> 
  6.     <!-- For settings and future properties files --> 
  7. <bean id="propertyConfigurer" 
  8.     class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> 
  9.     <property name="locations"> 
  10.         <list> 
  11.             <value>classpath:jdbc.properties</value> 
  12.         </list> 
  13.     </property> 
  14. </bean> 
  15.     <!--  
  16.         DriverManagerDataSource不具备连接池的功能 <bean id="dataSource" 
  17.         class="org.springframework.jdbc.datasource.DriverManagerDataSource" 
  18.         destroy-method="close"> <property name="driverClassName" 
  19.         value="${hibernate.connection.driver_class}"/> <property name="url" 
  20.         value="${hibernate.connection.url}"/> <property name="username" 
  21.         value="${hibernate.connection.username}"/> <property name="password" 
  22.         value="${hibernate.connection.password}"/> <property name="maxActive" 
  23.         value="100"/> <property name="maxWait" value="1000"/> <property 
  24.         name="poolPreparedStatements" value="true"/> <property 
  25.         name="defaultAutoCommit" value="true"/> </bean> 
  26.     --> 
  27. <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" 
  28.     destroy-method="close"> 
  29.     <property name="driverClass"> 
  30.         <value>${hibernate.connection.driver_class}</value> 
  31.     </property> 
  32.     <property name="jdbcUrl"> 
  33.         <value>${hibernate.connection.url}</value> 
  34.     </property> 
  35.     <property name="user"> 
  36.         <value>${hibernate.connection.username}</value> 
  37.     </property> 
  38.     <property name="password"> 
  39.         <value>${hibernate.connection.password}</value> 
  40.     </property> 
  41.  
  42.     <!--连接池中保留的最小连接数。--> 
  43.     <property name="minPoolSize"> 
  44.         <value>5</value> 
  45.     </property> 
  46.  
  47.     <!--连接池中保留的最大连接数。Default: 15 --> 
  48.     <property name="maxPoolSize"> 
  49.         <value>30</value> 
  50.     </property> 
  51.  
  52.     <!--初始化时获取的连接数,取值应在minPoolSize与maxPoolSize之间。Default: 3 --> 
  53.     <property name="initialPoolSize"> 
  54.         <value>10</value> 
  55.     </property> 
  56.  
  57.     <!--最大空闲时间,60秒内未使用则连接被丢弃。若为0则永不丢弃。Default: 0 --> 
  58.     <property name="maxIdleTime"> 
  59.         <value>60</value> 
  60.     </property> 
  61.  
  62.     <!--当连接池中的连接耗尽的时候c3p0一次同时获取的连接数。Default: 3 --> 
  63.     <property name="acquireIncrement"> 
  64.         <value>5</value> 
  65.     </property> 
  66.  
  67.     <!--  
  68.         JDBC的标准参数,用以控制数据源内加载的PreparedStatements数量。但由于预缓存的statements  
  69.         属于单个connection而不是整个连接池。所以设置这个参数需要考虑到多方面的因素。  
  70.         如果maxStatements与maxStatementsPerConnection均为0,则缓存被关闭。Default: 0  
  71.     --> 
  72.     <property name="maxStatements"> 
  73.         <value>150</value> 
  74.     </property> 
  75.  
  76.     <!--每60秒检查所有连接池中的空闲连接。Default: 0 --> 
  77.     <property name="idleConnectionTestPeriod"> 
  78.         <value>60</value> 
  79.     </property> 
  80.  
  81.     <!--定义在从数据库获取新连接失败后重复尝试的次数。Default: 30 --> 
  82.     <property name="acquireRetryAttempts"> 
  83.         <value>30</value> 
  84.     </property> 
  85.  
  86.     <!--  
  87.         获取连接失败将会引起所有等待连接池来获取连接的线程抛出异常。但是数据源仍有效  
  88.         保留,并在下次调用getConnection()的时候继续尝试获取连接。如果设为true,那么在尝试  
  89.         获取连接失败后该数据源将申明已断开并永久关闭。Default: false  
  90.     --> 
  91.     <property name="breakAfterAcquireFailure"> 
  92.         <value>true</value> 
  93.     </property> 
  94.  
  95.     <!--  
  96.         因性能消耗大请只在需要的时候使用它。如果设为true那么在每个connection提交的  
  97.         时候都将校验其有效性。建议使用idleConnectionTestPeriod或automaticTestTable  
  98.         等方法来提升连接测试的性能。Default: false  
  99.     --> 
  100.     <property name="testConnectionOnCheckout"> 
  101.         <value>false</value> 
  102.     </property> 
  103. </bean> 
  104. <bean id="sessionFactory" 
  105.     class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"> 
  106.     <property name="dataSource" ref="dataSource" /> 
  107.     <property name="configLocation" value="classpath:hibernate.cfg.xml" /> 
  108.  
  109.     <property name="hibernateProperties"> 
  110.         <props> 
  111.             <prop key="hibernate.dialect">${hibernate.dialect}</prop> 
  112.             <prop key="hibernate.cache.use_second_level_cache">false</prop> 
  113.             <prop key="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</prop> 
  114.             <prop key="show_sql">true</prop> 
  115.         </props> 
  116.     </property> 
  117. </bean> 
  118.   <bean id="transactionManager" 
  119.         class="org.springframework.orm.hibernate3.HibernateTransactionManager"> 
  120.         <property name="sessionFactory" ref="sessionFactory" /> 
  121.     </bean> 
  122. </beans> 
 
2、创建单元测试类
在src\test\java\com\ceair\app创建UserDaoTest.java,代码如下:
 
  
  1. package com.ceair.app;  
  2.  
  3.  
  4.  
  5.  
  6. import org.junit.Test;  
  7. import org.springframework.beans.factory.annotation.Autowired;  
  8.  
  9. import com.ceair.app.dao.BaseDaoTestCase;  
  10. import com.ceair.app.dao.GenericDao;  
  11. import com.ceair.app.model.User;  
  12.  
  13. public class UserDaoTest extends BaseDaoTestCase {  
  14.    @Autowired 
  15.     private GenericDao<User, Long> userDao;  
  16.       
  17.  
  18.       
  19.     @Test 
  20.     public void testUserById() throws Exception {  
  21.         User people = userDao.get(new Long(-2));  
  22.         assertEquals("管理员", people.getName());  
  23.     }  
  24.     
  25.     @Test 
  26.      public void testAddAndRemovePerson() throws Exception {  
  27.         User person = new User();  
  28.         person.setId(new Long(3));  
  29.         person.setName("陈小林");  
  30.         person = userDao.save(person);  
  31.         flush();  
  32.  
  33.         person = userDao.get(person.getId());  
  34.  
  35.         assertEquals("陈小林", person.getName());  
  36.         assertNotNull(person.getId());  
  37.         person.setName("陈小");  
  38.           
  39.         flush();  
  40.           
  41.         person = userDao.get(person.getId());  
  42.         assertEquals("陈小", person.getName());  
  43.         assertNotNull(person.getId());  
  44.         
  45.     
  46.     }  
  47.     
  48. }  
注意: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,代码如下:
 
 
  
  1. package com.ceair.app.dao;  
  2.  
  3. import java.util.List;  
  4.  
  5. import org.hibernate.criterion.DetachedCriteria;  
  6.  
  7. import com.ceair.app.model.User;  
  8.  
  9. public interface UserDao extends GenericDao<User, Long> {  
  10.     public List<User> findByCrteria(DetachedCriteria crteria);  
  11.       
  12.     public void changeName(String id,String newName) throws Exception;  
  13. }  
2、在src\main\java\com\ceair\app\dao\hibernate下创建UserDaoHibernate.java,代码如下:
 
 
  
  1. package com.ceair.app.dao.hibernate;  
  2.  
  3. import java.util.List;  
  4.  
  5. import org.hibernate.criterion.DetachedCriteria;  
  6. import org.hibernate.criterion.Restrictions;  
  7. import org.springframework.transaction.annotation.Isolation;  
  8. import org.springframework.transaction.annotation.Propagation;  
  9. import org.springframework.transaction.annotation.Transactional;  
  10.  
  11. import com.ceair.app.dao.UserDao;  
  12. import com.ceair.app.model.User;  
  13. public class UserDaoHibernate extends GenericDaoHibernate<User, Long> implements UserDao {  
  14.  
  15.     public UserDaoHibernate() {  
  16.         super(User.class);  
  17.     }  
  18.  
  19.     @SuppressWarnings("unchecked")  
  20.     //@Transactional(readOnly = true, propagation=Propagation.SUPPORTS)  执行读操作的时候,避免使用@Transactional,如果要使用,那就如此所示用法  
  21.     public List<User> findByCrteria(DetachedCriteria crteria) {  
  22.          return getHibernateTemplate().findByCriteria(crteria);  
  23.     }  
  24.       
  25.     /*  
  26.      * Required : 如果在一个事务中调用,就将该方法加到此事务中,如果没有启动事务,就创建新事务  
  27.      *   
  28.      * RequiredNew : 不管当前有没有事务,都启动新事务,如果有,会被挂起,直到此方法结束  
  29.      *   
  30.      * NotSupported : 不能在事务中执行此方法,如果有事务,会被挂起,直到此方法结束  
  31.      *   
  32.      * Supports : 如果有当前事务,此方法回加到当前事务,如果没有,容器不会启动新事务  
  33.      *   
  34.      * Mandatory : 必须在事务中执行此方法,否则会抛出异常 : TransactionRequiredException  
  35.      *   
  36.      * Never : 必须不在事务中调用此方法,否则抛出RemoteException(远程调用)或EJBException(本地调用)  
  37.      *   
  38.      */ 
  39.     @Transactional(  
  40.  
  41.             propagation = Propagation.REQUIRED,  
  42.  
  43.             isolation = Isolation.DEFAULT,//锁级别  
  44.  
  45.             rollbackFor = Exception.class 
  46.  
  47.         )  
  48.     public void changeName(String id, String newName) throws Exception {  
  49.         DetachedCriteria crteria = DetachedCriteria.forClass(User.class);  
  50.         crteria.add(Restrictions.eq("id"new Long(id)));  
  51.         List<User> userList = getHibernateTemplate().findByCriteria(crteria);  
  52.         User user = userList.get(0);  
  53.         user.setName(newName);  
  54.         getHibernateTemplate().update(user);  
  55.     //  throw new Exception();//rollback   
  56.     }  
  57.  
  58. }  
  59.  
注意:1、去掉55行(  throw new Exception();//rollback    )的注释,会回滚数据库操作。
3、修改\src\main\resources下的applicationContext.xml文件,修改后文件内容如下:
 
  
  1. <?xml version="1.0" encoding="UTF-8"?> 
  2. <beans xmlns="http://www.springframework.org/schema/beans" 
  3.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  4.     xmlns:context="http://www.springframework.org/schema/context" 
  5.     xmlns:tx="http://www.springframework.org/schema/tx" 
  6.     xsi:schemaLocation="http://www.springframework.org/schema/beans   
  7.            http://www.springframework.org/schema/beans/spring-beans-2.5.xsd  
  8.            http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd  
  9.            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd"> 
  10.     <!-- Example of SAF2 action instantiated by Spring --> 
  11.     <bean id="helloWorldAction"  class="com.ceair.app.HelloWorldAction" /> 
  12.      <bean id="userAction"  class="com.ceair.app.action.UserAction"/>    
  13.       <context:annotation-config/>    
  14.       <context:component-scan base-package="com.ceair"/>    
  15.       <tx:annotation-driven transaction-manager="transactionManager"/> 
  16. </beans> 
  17.  
注意: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文件,修改后文件内容如下:
 
  
  1. <?xml version="1.0" encoding="UTF-8"?> 
  2. <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  3.        xmlns:jee="http://www.springframework.org/schema/jee" 
  4.        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd  
  5.             http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.0.xsd"> 
  6.                
  7.     <bean id="userCustomDao" class="com.ceair.app.dao.hibernate.UserDaoHibernate" >   
  8.       <property name="sessionFactory" ref="sessionFactory"/> 
  9.      </bean> 
  10. </beans> 
注意:1、增加7-8行,把原来的Bean  userDao删除了
5、修改src\test\java\com\ceair\app下的UserDaoTest.java,修改后内容如下:
 
  
  1. package com.ceair.app;  
  2.  
  3.  
  4.  
  5.  
  6. import org.junit.Test;  
  7. import org.springframework.beans.factory.annotation.Autowired;  
  8.  
  9. import com.ceair.app.dao.BaseDaoTestCase;  
  10. import com.ceair.app.dao.UserDao;  
  11. import com.ceair.app.model.User;  
  12.  
  13. public class UserDaoTest extends BaseDaoTestCase {  
  14.    @Autowired 
  15.     private  UserDao userCustomDao;  
  16.       
  17.       
  18.     @Test 
  19.     public void testUserById() throws Exception {  
  20.         User people = userCustomDao.get(new Long(-2));  
  21.         assertEquals("管理员", people.getName());  
  22.     }  
  23.     @Test 
  24.     public void testAddAndRemovePerson() throws Exception {  
  25.         User person = new User();  
  26.         person.setId(new Long(3));  
  27.         person.setName("陈小林");  
  28.         person = userCustomDao.save(person);  
  29.         flush();  
  30.  
  31.         person = userCustomDao.get(person.getId());  
  32.  
  33.         assertEquals("陈小林", person.getName());  
  34.         assertNotNull(person.getId());  
  35.         person.setName("陈小");  
  36.           
  37.         flush();  
  38.           
  39.         person = userCustomDao.get(person.getId());  
  40.         assertEquals("陈小", person.getName());  
  41.         assertNotNull(person.getId());  
  42.         log.debug("removing person...");  
  43.  
  44.     }  
  45. }  
 6、在src\test\java\com\ceair\app下创建UserCustomDaoTest.java,代码内容如下:
 
  
  1. package com.ceair.app;  
  2.  
  3.  
  4.  
  5.  
  6. import java.util.List;  
  7.  
  8. import org.hibernate.criterion.DetachedCriteria;  
  9. import org.hibernate.criterion.Restrictions;  
  10. import org.junit.Test;  
  11. import org.springframework.beans.factory.annotation.Autowired;  
  12.  
  13. import com.ceair.app.dao.BaseDaoTestCase;  
  14. import com.ceair.app.dao.UserDao;  
  15. import com.ceair.app.model.User;  
  16.  
  17. public class UserCustomDaoTest extends BaseDaoTestCase {  
  18.       
  19.    @Autowired 
  20.    private UserDao userCustomDao;  
  21.       
  22.     @Test 
  23.     public void testFindByCrteria() throws Exception {  
  24.         DetachedCriteria crteria=DetachedCriteria.forClass(User.class);  
  25.         crteria.add(Restrictions.eq("name""管理员"));  
  26.         List<User> userList=userCustomDao.findByCrteria(crteria);  
  27.           
  28.         assertEquals("管理员", userList.get(0).getName());  
  29.         assertEquals(new Long(-2), userList.get(0).getId());  
  30.     }  
  31.    
  32.     @Test 
  33.     public void testChangeName() throws Exception {  
  34.         userCustomDao.changeName("-2""新管理员");  
  35.         flush();  
  36.         User people = userCustomDao.get(new Long(-2));  
  37.         assertEquals("新管理员", people.getName());  
  38.     }  
  39. }  
注意:1、事务测试前可以把BaseDaoTestCase.java59行(不回滚测试产生的数据)和UserDaoHibernate .java55行(抛出异常,回滚事务)和的注释去除,然后去数据库中查看是否更改名字成功。
以上工作全部完成后,打开命令行进入项目目录运行mvn test就可以运行单元测试。
关于事务的深入研究可以查看 http://www.ibm.com/developerworks/cn/java/j-ts1.html