[转]Spring的事务管理难点剖析(1):DAO和事务管理的牵绊

原文地址:http://stamen.iteye.com/blog/1441758

有些人很少使用Spring而不使用Spring事务管理器的应用,因此常常有人会问:是否用了Spring,就一定要用Spring事务管理器,否则就无法进行数据的持久化操作呢?事务管理器和DAO是什么关系呢?
  也许是DAO和事务管理如影随行的缘故吧,这个看似简单的问题实实在在地存在着,从初学者心中涌出,萦绕在老手的脑际。答案当然是否定的!我们都知道:事 务管理是保证数据操作的事务性(即原子性、一致性、隔离性、持久性,即所谓的ACID),脱离了事务性,DAO照样可以顺利地进行数据的操作。

  JDBC访问数据库

  下面,我们来看一段使用Spring JDBC进行数据访问的代码:
 

Java代码  收藏代码
  1. package com.baobaotao.withouttx.jdbc;  
  2.   
  3. import org.springframework.beans.factory.annotation.Autowired;  
  4. import org.springframework.jdbc.core.JdbcTemplate;  
  5. import org.springframework.stereotype.Service;  
  6. import org.springframework.context.ApplicationContext;  
  7. import org.springframework.context.support.ClassPathXmlApplicationContext;  
  8. import org.apache.commons.dbcp.BasicDataSource;  
  9.   
  10. @Service("userService")  
  11. public class UserJdbcWithoutTransManagerService {  
  12.     @Autowired  
  13.     private JdbcTemplate jdbcTemplate;  
  14.   
  15.     public void addScore(String userName,int toAdd){  
  16.         String sql = "UPDATE t_user u SET u.score = u.score + ? WHERE user_name =?";  
  17.         jdbcTemplate.update(sql,toAdd,userName);  
  18.     }  
  19.   
  20.     public static void main(String[] args) {  
  21.         ApplicationContext ctx = new     
  22.            ClassPathXmlApplicationContext("com/baobaotao/withouttx/jdbc/jdbcWithoutTx.xml");           
  23.         UserJdbcWithoutTransManagerService service =   
  24.                      (UserJdbcWithoutTransManagerService)ctx.getBean("userService");  
  25.         JdbcTemplate jdbcTemplate = (JdbcTemplate)ctx.getBean("jdbcTemplate");  
  26.         BasicDataSource basicDataSource = (BasicDataSource)jdbcTemplate.getDataSource();  
  27.   
  28.         //①检查数据源autoCommit的设置  
  29.         System.out.println("autoCommit:"+ basicDataSource.getDefaultAutoCommit());  
  30.   
  31.         //②插入一条记录,初始分数为10  
  32.         jdbcTemplate.execute("INSERT INTO t_user(user_name,password,score,last_logon_time)   
  33.                               VALUES('tom','123456',10,"+System.currentTimeMillis()+")");  
  34.   
  35.         //③调用工作在无事务环境下的服务类方法,将分数添加20分  
  36.         service.addScore("tom",20);  
  37.   
  38.          //④查看此时用户的分数  
  39.         int score = jdbcTemplate.queryForInt(  
  40.                   "SELECT score FROM t_user WHERE user_name ='tom'");  
  41.         System.out.println("score:"+score);  
  42.         jdbcTemplate.execute("DELETE FROM t_user WHERE user_name='tom'");  
  43.     }  
  44. }  
  45.     


  其中,jdbcWithoutTx.xml的配置文件如下所示:

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:p="http://www.springframework.org/schema/p"  
  6.        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
  7.     http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">       
  8.     <context:component-scan base-package="com.baobaotao.withouttx.jdbc"/>  
  9.   
  10.     <context:property-placeholder location="classpath:jdbc.properties"/>  
  11.     <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"  
  12.         destroy-method="close"   
  13.         p:driverClassName="${jdbc.driverClassName}"  
  14.         p:url="${jdbc.url}"   
  15.         p:username="${jdbc.username}"  
  16.         p:password="${jdbc.password}"/>  
  17.   
  18.     <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"  
  19.           p:dataSource-ref="dataSource"/>  
  20. </beans>  


运行UserJdbcWithoutTransManagerService,在控制台上打出如下的结果:

引用

defaultAutoCommit:true
score:30


   在jdbcWithoutTx.xml中,没有配置任何事务管理器,但是数据已经成功持久化到数据库中。在默认情况下,dataSource数据源的 autoCommit被设置为true——这也意谓着所有通过JdbcTemplate执行的语句马上提交,没有事务。如果将dataSource的 defaultAutoCommit设置为false,再次运行UserJdbcWithoutTransManagerService,将抛出错误,原 因是新增及更改数据的操作都没有提交到数据库,所以代码清单10-1④处的语句因无法从数据库中查询到匹配的记录而引发异常。
   对于强调读速度的应用,数据库本身可能就不支持事务:如使用MyISAM引擎的MySQL数据库。这时,无须在Spring应用中配置事务管理器,因为即使配置了,也是没有实际用处的。

Hibernate访问数据库

   对于Hibernate来说,情况就有点复杂了。因为Hibernate的事务管理拥有其自身的意义,它和Hibernate一级缓存在密切的关系:当我 们调用Session的save、update等方法时,Hibernate并不直接向数据库发送SQL语句,只在提交事务(commit)或flush 一级缓存时才真正向数据库发送SQL。所以,即使底层数据库不支持事务,Hibernate的事务管理也是有一定好处的,不会对数据操作的效率造成负面影 响。所以,如果是使用Hibernate数据访问技术,没有理由不配置HibernateTransactionManager事务管理器。
   但是,不使用Hibernate事务管理器,在Spring中,Hibernate照样也可以工作,来看下面的例子:

Java代码  收藏代码
  1. package com.baobaotao.withouttx.hiber;  
  2.   
  3. import org.springframework.beans.factory.annotation.Autowired;  
  4. import org.springframework.core.io.ClassPathResource;  
  5. import org.springframework.core.io.Resource;  
  6. import org.springframework.jdbc.core.JdbcTemplate;  
  7. import org.springframework.jdbc.core.simple.SimpleJdbcTemplate;  
  8. import org.springframework.stereotype.Service;  
  9. import org.springframework.context.ApplicationContext;  
  10. import org.springframework.context.support.ClassPathXmlApplicationContext;  
  11. import org.springframework.orm.hibernate3.HibernateTemplate;  
  12. import org.apache.commons.dbcp.BasicDataSource;  
  13. import org.springframework.test.jdbc.SimpleJdbcTestUtils;  
  14.   
  15. import com.baobaotao.User;  
  16.   
  17. @Service("hiberService")  
  18. public class UserHibernateWithoutTransManagerService {  
  19.       
  20.    @Autowired  
  21.     private HibernateTemplate hibernateTemplate;  
  22.   
  23.     public void addScore(String userName,int toAdd){  
  24.         User user = hibernateTemplate.get(User.class,userName);  
  25.         user.setScore(user.getScore()+toAdd);  
  26.         hibernateTemplate.update(user);  
  27.     }  
  28.   
  29.     public static void main(String[] args) {  
  30.         ApplicationContext ctx = new  
  31.              ClassPathXmlApplicationContext("com/baobaotao/withouttx/hiber/hiberWithoutTx.xml");  
  32.         UserHibernateWithoutTransManagerService service =   
  33.              (UserHibernateWithoutTransManagerService)ctx.getBean("hiberService");  
  34.   
  35.         JdbcTemplate jdbcTemplate = (JdbcTemplate)ctx.getBean("jdbcTemplate");  
  36.         BasicDataSource basicDataSource = (BasicDataSource)jdbcTemplate.getDataSource();  
  37.   
  38.         //①检查数据源autoCommit的设置  
  39.         System.out.println("autoCommit:"+ basicDataSource.getDefaultAutoCommit());  
  40.   
  41.         //②插入一条记录,初始分数为10  
  42.         jdbcTemplate.execute("INSERT INTO t_user(user_name,password,score,last_logon_time)   
  43.                             VALUES('tom','123456',10,"+System.currentTimeMillis()+")");  
  44.   
  45.         //③调用工作在无事务环境下的服务类方法,将分数添加20分  
  46.         service.addScore("tom",20);  
  47.           
  48.         //④查看此时用户的分数  
  49.         int score = jdbcTemplate.queryForInt(  
  50.                    "SELECT score FROM t_user WHERE user_name ='tom'");  
  51.         System.out.println("score:"+score);  
  52.         jdbcTemplate.execute("DELETE FROM t_user WHERE user_name='tom'");  
  53.     }  
  54. }  


此时,采用hiberWithoutTx.xml的配置文件,其配置内容如下所示:

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:p="http://www.springframework.org/schema/p"  
  6.        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
  7.     http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">  
  8.     <context:component-scan base-package="com.baobaotao.withouttx.hiber"/>  
  9.     …  
  10.     <bean id="sessionFactory"  
  11.           class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"  
  12.           p:dataSource-ref="dataSource">  
  13.         <property name="annotatedClasses">  
  14.             <list>  
  15.                 <value>com.baobaotao.User</value>  
  16.             </list>  
  17.         </property>  
  18.         <property name="hibernateProperties">  
  19.             <props>  
  20.                 <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>  
  21.                 <prop key="hibernate.show_sql">true</prop>  
  22.             </props>  
  23.         </property>  
  24.     </bean>  
  25.   
  26.     <bean id="hibernateTemplate"  
  27.           class="org.springframework.orm.hibernate3.HibernateTemplate"  
  28.           p:sessionFactory-ref="sessionFactory"/>  
  29.                   
  30. </beans>  


com.baobaotao.User是使用了Hibernate注解的领域对象,代码如下所示:

Java代码  收藏代码
  1. package com.baobaotao;  
  2.   
  3. import javax.persistence.Entity;  
  4. import javax.persistence.Table;  
  5. import javax.persistence.Column;  
  6. import javax.persistence.Id;  
  7. import java.lang.reflect.Field;  
  8. import java.io.Serializable;  
  9.   
  10. @Entity  
  11. @Table(name="T_USER")  
  12. public class User implements Serializable{  
  13.     @Id  
  14.     @Column(name = "USER_NAME")  
  15.     private String userName;  
  16.   
  17.     private String password;  
  18.   
  19.     private int score;  
  20.   
  21.     @Column(name = "LAST_LOGON_TIME")  
  22.     private long lastLogonTime = 0;  
  23.   
  24.     …  
  25. }  


   运行UserHibernateWithoutTransManagerService,程序正确执行,并得到类似于 UserJdbcWithoutTransManagerService的执行结果。这说明Hibernate在Spring中,在没有事务管理器的情况 下,依然可以正常地进行数据的访问。

  注:以上内容摘自《Spring 3.x企业应用开发实战》

posted @ 2016-03-02 22:51  dirgo  阅读(195)  评论(0编辑  收藏  举报