Spring 事务管理

事务的ACID特性:  
Atomic 原子性;Consistency 一致性;Isolation 隔离性; Durability 持久性。 
在常用的关系数据库中,依赖日志和锁机制来保证事务具有ACID特性 

事务的隔离级别:  
未提交读 read uncommitted 
提交读 read committed 
重复读 repeatable read 
序列化读 serializable 

隔离级别的效果:  
脏读: 
脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。 

不可重复读: 
是指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不可重复读。例如,一个编辑人员两次读取同一文档,但在两次读取之间,作者重写了该文档。当编辑人员第二次读取文档时,文档已更改。原始读取不可重复。如果只有在作者全部完成编写后编辑人员才可以读取文档,则可以避免该问题。 

幻读: 
是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样。例如,一个编辑人员更改作者提交的文档,但当生产部门将其更改内容合并到该文档的主复本时,发现作者已将未编辑的新材料添加到该文档中。如果在编辑人员和生产部门完成对原始文档的处理之前,任何人都不能将新材料添加到文档中,则可以避免该问题。 

不可重复读的重点是修改 : 
同样的条件 ,   你读取过的数据 ,   再次读取出来发现值不一样了 
幻读的重点在于新增或者删除 
同样的条件 ,   第 1 次和第 2 次读出来的记录数不一样 

基于元数据的 Spring 声明性事务 :  
Isolation 属性一共支持五种事务设置,具体介绍如下: 
DEFAULT 使用数据库设置的隔离级别 ( 默认 ) ,由 DBA 默认的设置来决定隔离级别 . 
READ_UNCOMMITTED 会出现脏读、不可重复读、幻读 ( 隔离级别最低,并发性能高 ) 
READ_COMMITTED  会出现不可重复读、幻读问题(锁定正在读取的行) 
REPEATABLE_READ 会出幻读(锁定所读取的所有行) 
SERIALIZABLE 保证所有的情况不会发生(锁表) 


Spring框架将所有事务管理都抽象为 PlatformTransactionManager、TransactionStatus、TransactionDefinition  3个接口 

PlatformTransactionManager 定义了事务管理器,所有与事务相关的操作都有其管理 
TransactionStatus 定义了事务的状态 
TransactionDefinition 定义了事务隔离级别和传播行为 
在启动事务时,PlatformTransactionManager根据TransactionDefinition来启动合适的事务 



编程式事务管理  
Java代码   收藏代码
  1. public class Main {  
  2.   
  3.   public static void main(String[] args) {  
  4.     HsqldbUtil.startDatabase();  
  5.     ApplicationContext context = new ClassPathXmlApplicationContext("config.xml");  
  6.     DataSource dataSource = (DataSource)context.getBean("dataSource");  
  7.     queryAllBooks(dataSource);  
  8.     // 获取PlatformTransactionManager:  
  9.     PlatformTransactionManager transManager = (PlatformTransactionManager)context.getBean("transactionManager");  
  10.     try {  
  11.       doTransaction(transManager, dataSource);  
  12.     }  
  13.     finally {  
  14.       queryAllBooks(dataSource);  
  15.       System.exit(0);  
  16.     }  
  17.   }  
  18.   
  19.   private static void doTransaction(PlatformTransactionManager transManager, DataSource dataSource) {  
  20.     // 定义TransactionDefinition:  
  21.     DefaultTransactionDefinition transDef = new DefaultTransactionDefinition();  
  22.     transDef.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);  
  23.     transDef.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);  
  24.     // 开始一个Transaction:  
  25.     TransactionStatus ts = transManager.getTransaction(transDef);  
  26.     try {  
  27.       insertBook(dataSource);  
  28.       //insertBook(dataSource);  
  29.       // 如果插入两次,则主键冲突  
  30.     }  
  31.     catch(RuntimeException e) {  
  32.       e.printStackTrace();  
  33.       // 回滚事务:  
  34.       transManager.rollback(ts);  
  35.       throw e;  
  36.     }  
  37.     catch(Error e) {  
  38.       e.printStackTrace();  
  39.       // 回滚事务:  
  40.       transManager.rollback(ts);  
  41.       throw e;  
  42.     }  
  43.     // 提交事务:  
  44.     transManager.commit(ts);  
  45.   }  
  46.   
  47.   private static void queryAllBooks(DataSource dataSource) {  
  48.     List<Book> books = new JdbcTemplate(dataSource).query("select * from Book"new BookRowMapper());  
  49.     System.err.println("-- All Books ---------------------------");  
  50.     for(Book book : books) {  
  51.       System.err.println("  " + book.getName() + ", by " + book.getAuthor());  
  52.     }  
  53.     System.err.println("----------------------------------------");  
  54.   }  
  55.   
  56.   private static void insertBook(DataSource dataSource) {  
  57.     JdbcTemplate jdbcTemp = new JdbcTemplate(dataSource);  
  58.     jdbcTemp.update(  
  59.         "insert into Book(id, name, author) values(?, ?, ?)",  
  60.         new Object[]{"123-456-789""New Book""New Author"});  
  61.   }  
  62. }  
  63.   
  64. class BookRowMapper implements RowMapper {  
  65.   
  66.   public Object mapRow(ResultSet rs, int rowNum) throws SQLException {  
  67.     Book book = new Book();  
  68.     book.setId(rs.getString("id"));  
  69.     book.setName(rs.getString("name"));  
  70.     book.setAuthor(rs.getString("author"));  
  71.     return book;  
  72.   }  
  73. }  


Java代码   收藏代码
  1. // 另一种方式无需捕获异常的方式,  
  2. TransactionTemplate tt = new TransactionTemplate(transManager);  
  3. tt.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);  
  4. tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);  
  5. tt.execute(new TransactionCallback() {  
  6.         public Object doInTransaction(TransactionStatus status) {  
  7.           return null;  
  8.         }  
  9.       });  


Xml代码   收藏代码
  1. <!-- 定义DataSource -->  
  2.   
  3. <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">  
  4.   <property name="driverClassName" value="org.hsqldb.jdbcDriver" />  
  5.   <property name="url" value="jdbc:hsqldb:mem:bookstore" />  
  6.   <property name="username" value="sa" />  
  7.   <property name="password" value="" />  
  8. </bean>  
  9.   
  10. <!-- 定义PlatformTransactionManager -->  
  11.   
  12. <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">  
  13.   <property name="dataSource" ref="dataSource" />  
  14. </bean>  
  15.   
  16. <!-- 使用JdbcTemplate -->  
  17.   
  18. <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">  
  19.   <property name="dataSource" ref="dataSource" />  
  20. </bean>  





如果要显式的回滚事务,没必要抛出RunTimeException,可以调用TransactionStatus.setRollBackOnly()方法 


声明式事务  
我们先使用Spring提供的TransactionProxyFactoryBean来实现具有声明式事务功能的BookDao, 
TransactionProxyFactoryBean是一个具有AOP代理功能的FactoryBean 
Xml代码   收藏代码
  1. <!-- 定义TransactionManager -->  
  2. <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">   
  3.   <property name="dataSource" ref="dataSource"/>  
  4. </bean>  
  5. <!-- 定义BookDao -->  
  6. <bean id="bookDaoTarget" class="example.chapter6.BookDaoImpl">  
  7.   <property name="dataSource" ref="dataSource" />  
  8. </bean>  
  9. <bean id="bookDao" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">  
  10.   <property name="target" ref="bookDaoTarget" />  
  11.   <property name="transactionManager" ref="transactionManager" />  
  12.   <property name="transactionAttributes">  
  13.     <props>  
  14.       <!-- 对以query开头的方法要求只读事务 -->  
  15.       <prop key="query*">PROPAGATION_REQUIRED,readOnly</prop>  
  16.       <!-- 对于其他方法要求事务 -->  
  17.       <prop key="*">PROPAGATION_REQUIRED</prop>  
  18.     </props>  
  19.   </property>  
  20. </bean>  


默认情况下,Spring只在RuntimeException异常或Error抛出时回滚事务,如果抛出CheckedException异常,Spring仍会提交事务 
如果需要回滚,则在TransactionProxyFactoryBean配置中显示添加 
Xml代码   收藏代码
  1. <prop key="*">PROPAGATION_REQUIRED,-RemoteException</prop>  



使用<tx:>简化配置,此时客户端自动获得的是具有事务功能的aop代理对象,不能获得上例中的bookDaoTarget 
Xml代码   收藏代码
  1. <!-- 声明TxAdvice -->  
  2. <tx:advice id="txAdvice" transaction-manager="transactionManager">  
  3.   <tx:attributes>  
  4.     <!-- 对以query开头的方法要求只读事务 -->  
  5.     <tx:method name="query*" read-only="true" />  
  6.     <!-- 对于其他方法要求事务 -->  
  7.     <tx:method name="*" rollback-for="java.io.IOException" />  
  8.   </tx:attributes>  
  9. </tx:advice>  
  10.   
  11. <aop:config>  
  12.   <!-- 使用AspectJ语法定义Pointcut,需要导入aspectjweaver.jar -->  
  13.   <aop:pointcut id="bookDaoOperation" expression="execution(* example.chapter6.BookDao.*(..))" />  
  14.   <!-- 织入 -->  
  15.   <aop:advisor advice-ref="txAdvice" pointcut-ref="bookDaoOperation" />  
  16. </aop:config>  



基于Java5注解简化配置  
配置文件中加入 
Xml代码   收藏代码
  1. <!-- 将所有具有@Transactional注解的Bean自动配置为声明式事务支持 -->  
  2. <tx:annotation-driven transaction-manager="transactionManager"/>  


在方法上添加@Transactional注解, 
如果加到接口上,只支持java动态代理,如果加到实现类上,还能支持CGlib 




 
总结如下: 

    Spring配置文件中关于事务配置总是由三个组成部分,分别是DataSource、TransactionManager和代理机制这三部分,无论哪种配置方式,一般变化的只是代理机制这部分。 
    DataSource、TransactionManager这两部分只是会根据数据访问方式有所变化,比如使用Hibernate进行数据访问时,DataSource实际为SessionFactory,TransactionManager的实现为 HibernateTransactionManager。 

根据代理机制的不同,总结了五种Spring事务的配置方式,配置文件如下: 
第一种方式:每个Bean都有一个代理 
Xml代码   收藏代码
  1. <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">  
  2.   <property name="configLocation" value="classpath:hibernate.cfg.xml" />  
  3.   <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />  
  4. </bean>  
  5.   
  6. <!-- 定义事务管理器(声明式的事务) -->  
  7. <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">  
  8.   <property name="sessionFactory" ref="sessionFactory" />  
  9. </bean>  
  10.   
  11. <!-- 配置DAO -->  
  12. <bean id="userDaoTarget" class="com.bluesky.spring.dao.UserDaoImpl">  
  13.   <property name="sessionFactory" ref="sessionFactory" />  
  14. </bean>  
  15.   
  16. <bean id="userDao" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">  
  17.   <!-- 配置事务管理器 -->  
  18.   <property name="transactionManager" ref="transactionManager" />  
  19.   <property name="target" ref="userDaoTarget" />  
  20.   <property name="proxyInterfaces" value="com.bluesky.spring.dao.GeneratorDao" />  
  21.   <!-- 配置事务属性 -->  
  22.   <property name="transactionAttributes">  
  23.     <props>  
  24.       <prop key="*">PROPAGATION_REQUIRED</prop>  
  25.     </props>  
  26.   </property>  
  27. </bean>  




第二种方式:所有Bean共享一个代理基类 
Xml代码   收藏代码
  1. <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">  
  2.   <property name="configLocation" value="classpath:hibernate.cfg.xml" />  
  3.   <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />  
  4. </bean>  
  5.   
  6. <!-- 定义事务管理器(声明式的事务) -->  
  7. <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">  
  8.   <property name="sessionFactory" ref="sessionFactory" />  
  9. </bean>  
  10.   
  11. <bean id="transactionBase" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"  
  12.   lazy-init="true" abstract="true">  
  13.   <!-- 配置事务管理器 -->  
  14.   <property name="transactionManager" ref="transactionManager" />  
  15.   <!-- 配置事务属性 -->  
  16.   <property name="transactionAttributes">  
  17.     <props>  
  18.       <prop key="*">PROPAGATION_REQUIRED</prop>  
  19.     </props>  
  20.   </property>  
  21. </bean>  
  22.   
  23. <!-- 配置DAO -->  
  24. <bean id="userDaoTarget" class="com.bluesky.spring.dao.UserDaoImpl">  
  25.   <property name="sessionFactory" ref="sessionFactory" />  
  26. </bean>  
  27.   
  28. <bean id="userDao" parent="transactionBase">  
  29.   <property name="target" ref="userDaoTarget" />  
  30. </bean>  




第三种方式:使用拦截器 
Xml代码   收藏代码
  1. <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">  
  2.   <property name="configLocation" value="classpath:hibernate.cfg.xml" />  
  3.   <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />  
  4. </bean>  
  5.   
  6. <!-- 定义事务管理器(声明式的事务) -->  
  7. <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">  
  8.   <property name="sessionFactory" ref="sessionFactory" />  
  9. </bean>  
  10.   
  11. <bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor">  
  12.   <property name="transactionManager" ref="transactionManager" />  
  13.   <!-- 配置事务属性 -->  
  14.   <property name="transactionAttributes">  
  15.     <props>  
  16.       <prop key="*">PROPAGATION_REQUIRED</prop>  
  17.     </props>  
  18.   </property>  
  19. </bean>  
  20.   
  21. <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">  
  22.   <property name="beanNames">  
  23.     <list>  
  24.       <value>*Dao</value>  
  25.     </list>  
  26.   </property>  
  27.   <property name="interceptorNames">  
  28.     <list>  
  29.       <value>transactionInterceptor</value>  
  30.     </list>  
  31.   </property>  
  32. </bean>  
  33.   
  34. <!-- 配置DAO -->  
  35. <bean id="userDao" class="com.bluesky.spring.dao.UserDaoImpl">  
  36.   <property name="sessionFactory" ref="sessionFactory" />  
  37. </bean>  




第四种方式:使用tx标签配置的拦截器 
Xml代码   收藏代码
  1. <context:annotation-config />  
  2. <context:component-scan base-package="com.bluesky" />  
  3.   
  4. <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">  
  5.   <property name="configLocation" value="classpath:hibernate.cfg.xml" />  
  6.   <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />  
  7. </bean>  
  8.   
  9. <!-- 定义事务管理器(声明式的事务) -->  
  10. <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">  
  11.   <property name="sessionFactory" ref="sessionFactory" />  
  12. </bean>  
  13.   
  14. <tx:advice id="txAdvice" transaction-manager="transactionManager">  
  15.   <tx:attributes>  
  16.     <tx:method name="add*" propagation="REQUIRED" />  
  17.     <tx:method name="delete*" propagation="REQUIRED" />  
  18.     <tx:method name="update*" propagation="REQUIRED" />  
  19.     <tx:method name="*" read-only="true" />  
  20.   </tx:attributes>  
  21. </tx:advice>  
  22.   
  23. <aop:config>  
  24.   <aop:pointcut id="interceptorPointCuts" expression="execution(* com.bluesky.spring.dao.*.*(..))" />  
  25.   <aop:advisor advice-ref="txAdvice" pointcut-ref="interceptorPointCuts" />  
  26. </aop:config>  


第五种方式:全注解 
Xml代码   收藏代码
  1. <context:annotation-config />  
  2. <context:component-scan base-package="com.bluesky" />  
  3.   
  4. <tx:annotation-driven transaction-manager="transactionManager" />  
  5.   
  6. <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">  
  7.   <property name="configLocation" value="classpath:hibernate.cfg.xml" />  
  8.   <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />  
  9. </bean>  
  10.   
  11. <!-- 定义事务管理器(声明式的事务) -->  
  12. <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">  
  13.   <property name="sessionFactory" ref="sessionFactory" />  
  14. </bean>  

此时在DAO上需加上@Transactional注解,如下: 

Java代码   收藏代码
  1. @Transactional  
  2. @Component("userDao")  
  3. public class UserDaoImpl extends HibernateDaoSupport implements UserDao {  
  4.   
  5.     public List<User> listUsers() {  
  6.         return this.getSession().createQuery("from User").list();  
  7.     }  
  8. }  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值