spring事务传播机制实例讲解

天温习spring的事务处理机制,总结以下javascript

 

 

  1. 对于SQL事务的概念以及ACID性质,能够参见个人另外一篇博文 http://kingj.iteye.com/admin/blogs/1675011
  2. spring的管理的事务能够分为以下2类
    1. 逻辑事务   在spring中定义的事务一般指逻辑事务,提供比物理事务更抽象,方便的事务配置管理,但也基于物理事务
    2. 物理事务  特定于数据库的事务
  3. spring中支持一下2中事务声明方式
    1. 编程式事务  当系统须要明确的,细粒度的控制各个事务的边界,应选择编程式事务
    2. 声明式事务  当系统对于事务的控制粒度较粗时,应该选择申明式事务
    3. 不管你选择上述何种事务方式去实现事务控制,spring都提供基于门面设计模式的事务管理器供选择,以下是spring事务中支持的事务管理器
    4. 事务管理器实现(org.springframework.*) 使用时机
      jdbc.datasource.DataSourceTransactionManager 使用jdbc的抽象以及ibatis支持
      orm.hibernate.HibernateTransactionManager 使用hibernate支持(默认3.0如下版本)
      orm.hibernate3.HibernateTransactionManager 使用hibernate3支持
      transaction.jta.JtaTransactionManager 使用分布式事务(分布式数据库支持)
      orm.jpa.JpaTransactionManager 使用jpa作为持久化工具
      orm.toplink.TopLinkTransactionManager 使用TopLink持久化工具
      orm.jdo.JdoTransactionManager 使用Jdo持久化工具
      jms.connection.JmsTransactionManager 使用JMS 1.1+
      jms.connection.JmsTransactionManager102 使用JMS 1.0.2
      transaction.jta.OC4JJtaTransactionManager 使用oracle的OC4J JEE容器
      transaction.jta.WebLogicJtaTransactionManager 在weblogic中使用分布式数据库
      jca.cci.connection.CciLocalTransactionManager 使用jrping对J2EE Connector Architecture (JCA)和Common Client Interface (CCI)的支持

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

UML结构图以下java

 



  四、各类事务管理器的定义以下 web

  •  
    • JdbcTransactionManager定义以下
    • Xml代码   收藏代码
      1. <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">  
      2.           <property name="dataSource" ref="dataSource"/>  
      3. </bean>  
        
    • hibernate事务管理器配置以下
    • Xml代码   收藏代码
      1. <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">  
      2.     <property name="sessionFactory" ref="sessionFactory"/><span style="white-space: pre;">                                </span>  
      3. </bean>  
    • hibernate的事务管理器会注入session会话工厂,而后将事务处理委托给当前的transaction对象,事务提交时,调用commit()方法,回滚时调用rollback()方法
    • jpa事务管理器配置以下
    • Xml代码   收藏代码
      1. <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">  
      2. <span style="white-space: pre;">    </span><property name="entityManagerFactory" ref="entityManagerFactory"/>  
      3. </bean>  
       
    • 其他事务管理器可参见spring in action中说明
  • 五、申明式事务配置
    • spring特有的事务传播行为,spring支持7种事务传播行为,肯定客户端和被调用端的事务边界(说得通俗一点就是多个具备事务控制的service的相互调用时所造成的复杂的事务边界控制)下图所示为7钟事务传播机制
    • 传播行为 含义
      PROPAGATION_REQUIRED(XML文件中为REQUIRED) 表示当前方法必须在一个具备事务的上下文中运行,若有客户端有事务在进行,那么被调用端将在该事务中运行,不然的话从新开启一个事务。(若是被调用端发生异常,那么调用端和被调用端事务都将回滚)
      PROPAGATION_SUPPORTS(XML文件中为SUPPORTS) 表示当前方法没必要须要具备一个事务上下文,可是若是有一个事务的话,它也能够在这个事务中运行
      PROPAGATION_MANDATORY(XML文件中为MANDATORY) 表示当前方法必须在一个事务中运行,若是没有事务,将抛出异常
      PROPAGATION_NESTED(XML文件中为NESTED) 表示若是当前方法正有一个事务在运行中,则该方法应该运行在一个嵌套事务中,被嵌套的事务能够独立于被封装的事务中进行提交或者回滚。若是封装事务存在,而且外层事务抛出异常回滚,那么内层事务必须回滚,反之,内层事务并不影响外层事务。若是封装事务不存在,则同PROPAGATION_REQUIRED的同样
      PROPAGATION_NEVER(XML文件中为NEVER) 表示当方法务不该该在一个事务中运行,若是存在一个事务,则抛出异常
      PROPAGATION_REQUIRES_NEW(XML文件中为REQUIRES_NEW) 表示当前方法必须运行在它本身的事务中。一个新的事务将启动,并且若是有一个现有的事务在运行的话,则这个方法将在运行期被挂起,直到新的事务提交或者回滚才恢复执行。
      PROPAGATION_NOT_SUPPORTED(XML文件中为NOT_SUPPORTED) 表示该方法不该该在一个事务中运行。若是有一个事务正在运行,他将在运行期被挂起,直到这个事务提交或者回滚才恢复执行
  • 六、spring中的事务隔离级别
    • spring的事务隔离级别其实本质上是对SQL92标准的4种事务隔离级别的一种封装,具体参加个人博文:http://kingj.iteye.com/admin/blogs/1675011
    • spring的事务隔离级别以下表所示
    • 隔离级别 含义
      ISOLATION_DEFAULT 使用数据库默认的事务隔离级别
      ISOLATION_READ_UNCOMMITTED 容许读取还没有提交的修改,可能致使脏读、幻读和不可重复读
      ISOLATION_READ_COMMITTED 容许从已经提交的事务读取,可防止脏读、但幻读,不可重复读仍然有可能发生
      ISOLATION_REPEATABLE_READ 对相同字段的屡次读取的结果是一致的,除非数据被当前事务自生修改。可防止脏读和不可重复读,但幻读仍有可能发生
      ISOLATION_SERIALIZABLE 彻底服从ACID隔离原则,确保不发生脏读、不可重复读、和幻读,但执行效率最低。
  • 七、spring事务只读属性
    • spring事务只读的含义是指,若是后端数据库发现当前事务为只读事务,那么就会进行一系列的优化措施。它是在后端数据库进行实施的,所以,只有对于那些有可能启动一个新事务的传播行为(REQUIRED,REQUIRES_NEW,NESTED)的方法来讲,才有意义。(测试代表,当使用JDBC事务管理器并设置当前事务为只读时,并不能发生预期的效果,即能执行删除,更新,插入操做)
  • 八、spring的事务超时
    • 有的时候为了系统中关键部分的性能问题,它的事务执行时间应该尽量的短。所以能够给这些事务设置超时时间,以秒为单位。咱们知道事务的开始每每都会发生数据库的表锁或者被数据库优化为行锁,若是容许时间过长,那么这些数据会一直被锁定,影响系统的并发性。
    • 由于超时时钟是在事务开始的时候启动,所以只有对于那些有可能启动新事物的传播行为(REQUIRED,REQUIRES_NEW,NESTED)的方法来讲,事务超时才有意义。
  • 九、事务回滚规则
    • spring中能够指定当方法执行并抛出异常的时候,哪些异常回滚事务,哪些异常不回滚事务。
    • 默认状况下,只在方法抛出运行时异常的时候才回滚(runtime exception)。而在出现受阻异常(checked exception)时不回滚事务,这个ejb的回滚行为一致。
    • 固然能够采用申明的方式指定哪些受阻异常像运行时异常那样指定事务回滚。
  • 十、spring申明式事务配置
    • 将aop,tx命名空间添加到当前的spring配置文件头中
    • 定义一个事务AOP通知
    • Xml代码   收藏代码
      1. <tx:advice id="txAdvice" transactionManager="transactionManager">  
      2.      <tx:attributes>  
      3.          <tx:method name="add*" propagation="REQUIRED"/>  
      4.      </tx:attributes>  
      5. </tx:advice><span style="white-space: pre;">  </span>  
    • 定义一个事务切面,即应该在哪些类的哪些方法上面进行事务切入
    • Xml代码   收藏代码
      1. <aop:config>  
      2.     <aop:advisor pointcut="execution(* *..zx.spring.UserService*.*(..))||execution(* *..spring.ServiceFacade.*(..))||execution(* *..spring.BookService.*(..))" advice-ref="txAdvice"/>  
      3. </aop:config>  
       
  • 十一、结合具体的代码实现讲解spring的7种事务传播机制效果
    • 准备环境
    • 咱们采用JDBC+ORACLE实现具体操做,首先搭建好spring的开发环境,配置好数据源,创建好Test
    • 建立数据库结构,建立一个用户表和Book表
    • Sql代码   收藏代码
      1. CREATE TABLE T_USER(  
      2.     ID INT,  
      3.     NAME VARCHAR2(200)  
      4. );  
      5.   
      6. CREATE TABLE T_BOOK(  
      7.     ID INT ,  
      8.     NAME VARCHAR2(200)  
      9. );  
       
    • 搭建好的结构以下


    •  
    • Xml代码   收藏代码
      1. <!-- 定义数据源 -->  
      2. <bean id="ams" class="com.mchange.v2.c3p0.ComboPooledDataSource"  
      3.         destroy-method="close">  
      4.         <property name="driverClass" value="${jdbc.ams.driver}" />  
      5.         <property name="jdbcUrl" value="${jdbc.ams.url}" />  
      6.         <property name="user" value="${jdbc.ams.username}" />  
      7.         <property name="password" value="${jdbc.ams.password}" />  
      8.         <property name="initialPoolSize" value="${initialSize}" />  
      9.         <property name="minPoolSize" value="${minPoolSize}" />  
      10.         <property name="maxPoolSize" value="${maxActive}" />  
      11.         <property name="acquireIncrement" value="${acquireIncrement}" />  
      12.         <property name="maxIdleTime" value="${maxIdleTime}" />  
      13.     </bean>  
      14.   
      15. <!-- 定义jdbc模板类-->   
      16. <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">  
      17.         <property name="dataSource" ref="ams"/>  
      18.     </bean>  
       
  • 在spring包下面创建3个service
  • BookService
  • Java代码   收藏代码
    1. package com.zx.spring;  
    2.   
    3. import org.springframework.jdbc.core.JdbcTemplate;  
    4.   
    5. public class BookService {  
    6.     public static final String ADD_BOOK="insert into t_book(id,name) values(1,'duck-j2ee')";  
    7.     private JdbcTemplate jdbcTemplate;  
    8.       
    9.     public void addBook() throws Exception{  
    10.         this.jdbcTemplate.execute(ADD_BOOK);  
    11.         throw new RollbackException("跳出执行");  
    12.     }  
    13.   
    14.     public JdbcTemplate getJdbcTemplate() {  
    15.         return jdbcTemplate;  
    16.     }  
    17.   
    18.     public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {  
    19.         this.jdbcTemplate = jdbcTemplate;  
    20.     }  
    21. }  
     
  • UserService
  • Java代码   收藏代码
    1. package com.zx.spring;  
    2.   
    3. import org.springframework.jdbc.core.JdbcTemplate;  
    4.   
    5. public class UserService {  
    6.     public static final String ADD_USER="insert into t_user(id,name) values(1,'duck')";  
    7.       
    8.     private BookService bs;  
    9.     private JdbcTemplate jdbcTemplate;  
    10.       
    11.     public void addUser()throws Exception {  
    12.         this.bs.addBook();  
    13.         this.jdbcTemplate.execute(ADD_USER);  
    14.     }  
    15.   
    16.     public JdbcTemplate getJdbcTemplate() {  
    17.         return jdbcTemplate;  
    18.     }  
    19.   
    20.     public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {  
    21.         this.jdbcTemplate = jdbcTemplate;  
    22.     }  
    23.   
    24.     public BookService getBs() {  
    25.         return bs;  
    26.     }  
    27.   
    28.     public void setBs(BookService bs) {  
    29.         this.bs = bs;  
    30.     }  
    31. }  
     
  • 建立一个ServiceFacade门面,将UserService和BookService包装起来
  • Java代码   收藏代码
    1. package com.zx.spring;  
    2.   
    3. public class ServiceFacade {  
    4.     private BookService bs;  
    5.     private UserService us;  
    6.     public BookService getBs() {  
    7.         return bs;  
    8.     }  
    9.     public void setBs(BookService bs) {  
    10.         this.bs = bs;  
    11.     }  
    12.     public UserService getUs() {  
    13.         return us;  
    14.     }  
    15.     public void setUs(UserService us) {  
    16.         this.us = us;  
    17.     }  
    18.       
    19.     public void addUserBook()throws Exception{  
    20.         bs.addBook();  
    21.         us.addUser();  
    22.     }  
    23. }  
     
    • 上面咱们配置了3个service接口,并在spring中配置了申明式事务。
    • Xml代码   收藏代码
      1. <aop:config>  
      2.     <aop:advisor pointcut="execution(* *..zx.spring.UserService*.*(..))||execution(* *..spring.ServiceFacade.*(..))||execution(* *..spring.BookService.*(..))" advice-ref="txAdvice"/>  
      3. </aop:config>  
       
    • 咱们在一个pointcut中定义了3个aspectj方式的切入点,即对这3个类的全部方法进行事务切入。
    接下来咱们开始配置不一样的事务传播机制,来看看效果。
  • 到此,准备工做大功告成,接下来咱们来对7中传播机制作一个详细解释。
 
  • 一 :咱们来对单个方法的事务传播机制进行一个了解
    1. REQUIRED, REQUIRES_NEW
      1. junit代码
      2. Java代码   收藏代码
        1. @Test  
        2.    public void testAddBook()throws Exception{  
        3.        BookService bs=(BookService)this.getBean("bookService");  
        4.        bs.addBook();  
        5.    }  
         
      3. addBook()代码
      4. Java代码   收藏代码
        1. public void addBook() throws Exception{  
        2.       this.jdbcTemplate.execute(ADD_BOOK);  
        3.       throw new RuntimeException("throw runtime exception in outter transaction");  
        4.   }  
         
      5. 执行junit后,控制台以下
      6. Java代码   收藏代码
        1. 一、[DEBUG,JdbcTemplate,main] Executing SQL statement [insert into t_book(id,name) values(1,'duck-j2ee')]  
        2. 二、[DEBUG,DataSourceTransactionManager,main] Initiating transaction rollback  
        3. 三、[DEBUG,DataSourceTransactionManager,main] Rolling back JDBC transaction on Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@a2b392]  
         
      7. 能够知道,当addBook()方法的事务传播机制为REQUIRED, REQUIRES_NEW ,而且抛出运行时异常时,将会回滚事务。
      8. 当addBook()方法抛出受检查的异常时,将不会回滚事务。
      9. addBook()方法以下:
      10. Java代码   收藏代码
        1. public void addBook() throws Exception{  
        2.        this.jdbcTemplate.execute(ADD_BOOK);  
        3.        throw new Exception("throw runtime exception in outter transaction");  
        4.    }  
         
      11. Java代码   收藏代码
        1. 一、[DEBUG,DataSourceTransactionManager,main] Creating new transaction with name [com.zx.spring.BookService.addBook]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT  
        2. 二、[DEBUG,JdbcTemplate,main] Executing SQL statement [insert into t_book(id,name) values(1,'duck-j2ee')]  
        3. 三、[DEBUG,DataSourceTransactionManager,main] Initiating transaction commit  
        4. 四、[DEBUG,DataSourceTransactionManager,main] Committing JDBC transaction on Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@4310d0]  
         
      12. 第3行可知,事务提交,不回滚
    2. MANDATORY
      1. 代码同上所示,只是事务传播机制改成 MANDATORY 
      2. Xml代码   收藏代码
        1. <tx:advice id="txAdvice" transaction-manager="txManager">  
        2.     <tx:attributes>  
        3.         <tx:method name="addUser" propagation="REQUIRED" />  
        4.         <tx:method name="addBook" propagation="MANDATORY"/>  
        5.     </tx:attributes>  
        6. </tx:advice>  
         
      3. 运行junit后,因为单个方法执行没有指定任何事务传播机制,所以抛出异常。
      4. Java代码   收藏代码
        1. org.springframework.transaction.IllegalTransactionStateException: No existing transaction found for transaction marked with propagation 'mandatory'  
        2.     at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:357)  
         
    3. NESTED
      1. NESTED内嵌事务,若是没有外层事务,则新建一个事务,行为同REQUIRED同样
    4. NEVER
      1. NEVER不会以事务方式运行,执行junit代码后,控制台以下
      2. Java代码   收藏代码
        1. 一、[DEBUG,JdbcTemplate,main] Executing SQL statement [insert into t_book(id,name) values(1,'duck-j2ee')]  
        2. 二、[DEBUG,DataSourceUtils,main] Registering transaction synchronization for JDBC Connection  
        3. 三、[DEBUG,DataSourceTransactionManager,main] Should roll back transaction but cannot - no transaction available  
         
      3. 因为发生运行时异常,事务本应该回滚,可是在第三行能够知道,因为事务传播机制为NEVER,所以找不到事务进行回滚,数据库只添加了一条记录。


      4.  
    5. SUPPORTS
      1. 单个方法 调用时supports行为同NEVER同样,不会建立事务,只是若是有事务,则会加入到当前事务中去,具体的行为下面有分析。
    6. NOT_SUPPORTED
      1. 单个方法被执行时,不会建立事务。若是当前有事务,将封装事务挂起,知道该方法执行完成再恢复封装事务,继续执行。
 
 
二:在了解了单个方法的事务传播机制后,咱们来配置多个方法调用之间的传播机制的行为
  • 一、咱们将UserService和BookService中的addUser(),addBook()方法分别配置为REQUIRED,并在addBook()方法中抛出运行时异常。
  • Xml代码   收藏代码
    1. <tx:advice id="txAdvice" transaction-manager="txManager">  
    2.     <tx:attributes>  
    3.         <tx:method name="addUser" propagation="REQUIRED"/>  
    4.         <tx:method name="addBook" propagation="REQUIRED"/>  
    5.     </tx:attributes>  
    6. </tx:advice>   
    Xml代码   收藏代码
    1.   
    • Java代码   收藏代码
      1. 一、public void addUser()throws Exception {  
      2. 二、   this.bs.addBook();  
      3. 三、   this.jdbcTemplate.execute(ADD_USER);  
      4.     }  
       
    • Java代码   收藏代码
      1. 四、public void addBook() throws Exception{  
      2. 五、        this.jdbcTemplate.execute(ADD_BOOK);  
      3. 六、        throw new RuntimeException("跳出执行");  
      4.     }  
  • 测试用例以下
  • Java代码   收藏代码
    1. @Test  
    2.  public void testAddUser()throws Exception{  
    3.      UserService us=(UserService)this.getBean("userService");  
    4.      us.addUser();  
    5.  }  
  • 执行前数据库以下


  •  
  • 执行后,console控制台输出以下:
  • Java代码   收藏代码
    1. [DEBUG,DataSourceTransactionManager,main]  
    2.   
    3. Creating new transaction with name [com.zx.spring.UserService.addUser]:   
    Java代码   收藏代码
    1. PROPAGATION_REQUIRED,ISOLATION_DEFAULT  
     上面输出能够知道,spring自动给addUser()方法切入了事务,事务隔离级别为数据库默认级别。
  • 咱们再观察控制台输出
  • Java代码   收藏代码
    1. 一、[DEBUG,DataSourceTransactionManager,main] Acquired Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@1973fc5] for JDBC transaction  
    2. 二、[DEBUG,DataSourceTransactionManager,main] Switching JDBC Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@1973fc5] to manual commit  
    3. 三、[DEBUG,DataSourceTransactionManager,main] Participating in existing transaction  
    4. 四、[DEBUG,JdbcTemplate,main] Executing SQL statement [insert into t_book(id,name) values(1,'duck-j2ee')]  
    5. 五、[DEBUG,DataSourceTransactionManager,main] Participating transaction failed - marking existing transaction as rollback-only  
    6. 六、[DEBUG,DataSourceTransactionManager,main] Setting JDBC transaction [com.mchange.v2.c3p0.impl.NewProxyConnection@1973fc5] rollback-only  
    7. 七、[DEBUG,DataSourceTransactionManager,main] Initiating transaction rollback  
    8. 八、[DEBUG,DataSourceTransactionManager,main] Rolling back JDBC transaction on Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@1973fc5]  
     
  • 第一行jdbc事务管理器从c3p0链接池中获取一个连接
  • 第二行设置jdbc事务提交方式为手动提交
  • 代码执行到方法addUser()中第一行时,因为addUser()方法的事务级别为REQUIRED的所以,事务管理器开始了一个事务。执行到第二行addBook()时,因为addBook()方法的事务传播行为为REQUIRED的,咱们知道REQUIRED方式是若是有一个事务,则加入事务中,若是没有,则新建一个事务。由控制台输出的第3行能够知道,addBook方法加入到了addUser方法的事务当中去,接着第4行执行了插入t_book语句,因为addBook()方法在第6行时,抛出了运行时异常,所以当前事务失败,可从控制台输出第5行得知。
  • 因为addUser()和addBook()方法共享了一个事务,在addBook()方法中又抛出了运行时异常,所以事务必须回滚,这由数据库查询可知。


  •  
  • 若是咱们将addBook()方法中抛出的运行时异常改成checked异常的话,会是什么结果呢?
  • Java代码   收藏代码
    1. 七、public void addBook() throws Exception{  
    2. 八、        this.jdbcTemplate.execute(ADD_BOOK);  
    3. 九、        throw new Exception("跳出执行");  
    4.    }  
     
  • Java代码   收藏代码
    1. 九、[DEBUG,DataSourceTransactionManager,main] Acquired Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@1973fc5] for JDBC transaction  
    2. 十、[DEBUG,DataSourceTransactionManager,main] Switching JDBC Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@1973fc5] to manual commit  
    3. 十一、[DEBUG,DataSourceTransactionManager,main] Participating in existing transaction  
    4. 十二、[DEBUG,JdbcTemplate,main] Executing SQL statement [insert into t_book(id,name) values(1,'duck-j2ee')]  
    5. 1三、[DEBUG,DataSourceTransactionManager,main] Initiating transaction commit  
    6. 1四、[DEBUG,DataSourceTransactionManager,main] Releasing JDBC Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@457d21] after transaction  
     
  • 由控制台输出第13行能够知道,除了addBook()方法中抛出的检查异常被忽略以外,其它的同上面的一致。再看数据库能够知道,addBook()方法和被执行了,addUser()方法被抛出的检查异常终止调用。


  •  

  • 若是咱们给addUser()方法指定rollback-for属性,那么addBook()方法的事务回回滚吗?
  • Java代码   收藏代码
    1. public void addBook() throws Exception{  
    2.         this.jdbcTemplate.execute(ADD_BOOK);  
    3.         throw new Exception("跳出执行");  
    4.     }  
     
  • Java代码   收藏代码
    1. <tx:advice id="txAdvice" transaction-manager="txManager">  
    2.    <tx:attributes>  
    3.     <tx:method name="addUser" propagation="REQUIRED" <span style="color: #ff0000;"><span style="color: #888888;">rollback-for="Exception"</span></span>/>  
    4.     <tx:method name="addBook" propagation="REQUIRED"/>  
    5.    </tx:attributes>  
    6. </tx:advice>  
     
  • 控制台输出以下
  • Java代码   收藏代码
    1. 1五、[DEBUG,DataSourceTransactionManager,main] Participating in existing transaction  
    2. 1六、[DEBUG,JdbcTemplate,main] Executing SQL statement [insert into t_book(id,name) values(1,'duck-j2ee')]  
    3. 1七、[DEBUG,DataSourceTransactionManager,main] Initiating transaction rollback  
    4. 1八、[DEBUG,DataSourceTransactionManager,main] Rolling back JDBC transaction on Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@121d383]  
     可见,当指定了rollback-for属性时,只要抛出了指定的异常,事务就会回滚。
 
 
  • 二、上面咱们讨论了两个方法都指定为事务传播机制为REQUIRED,那么咱们来改变如下addBook()方的事务传播机制改成NEVER ,来看看它们的效果
    • Xml代码   收藏代码
      1. <tx:advice id="txAdvice" transaction-manager="txManager">  
      2.     <tx:attributes>  
      3.         <tx:method name="addUser" propagation="REQUIRED" />  
      4.         <tx:method name="addBook" propagation="NEVER"/>  
      5.     </tx:attributes>  
      6. </tx:advice>  
       执行Junit测试后发现,控制台输出以下:
    • Java代码   收藏代码
      1. org.springframework.transaction.IllegalTransactionStateException: Existing transaction found for transaction marked with propagation 'never'  
      2.     at org.springframework.transaction.support.AbstractPlatformTransactionManager.handleExistingTransaction(AbstractPlatformTransactionManager.java:399)  
      3.     at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:347)  
      4.     at org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:335)  
       Existing transaction found for transaction marked with propagation 'never',也就是说addBook不该该在事务中运行,可是addUser这个客户端调用者却有一个事务,所以报错。
 
  • 三、咱们接着将addBook()方法的事务传播机制改成MANDATORY
    • Xml代码   收藏代码
      1. <tx:advice id="txAdvice" transaction-manager="txManager">  
      2.     <tx:attributes>  
      3.         <tx:method name="addUser" propagation="REQUIRED" />  
      4.         <tx:method name="addBook" propagation="MANDATORY"/>  
      5.     </tx:attributes>  
      6. </tx:advice>  
      执行junit后,控制台输出以下
    • Java代码   收藏代码
      1. [DEBUG,DataSourceTransactionManager,main] Participating in existing transaction  
      2. [DEBUG,JdbcTemplate,main] Executing SQL statement [insert into t_book(id,name) values(1,'duck-j2ee')]  
      3. [DEBUG,JdbcTemplate,main] Executing SQL statement [insert into t_user(id,name) values(1,'duck')]  
      4. [DEBUG,DataSourceTransactionManager,main] Initiating transaction commit  
      能够知道当前和REQUIRED同样的传播行为。
 
  • 四、咱们将addBook()方法的事务传播机制改成NESTED-内嵌事务,那么传播机制之间会怎么互相影响呢?
    • Xml代码   收藏代码
      1. <tx:advice id="txAdvice" transaction-manager="txManager">  
      2.     <tx:attributes>  
      3.         <tx:method name="addUser" propagation="REQUIRED" />  
      4.         <tx:method name="addBook" propagation="NESTED"/>  
      5.     </tx:attributes>  
      6. </tx:advice>  
      Xml代码   收藏代码
      1. addBook()方法以下,依然抛出(checked-exception)检查异常。  
      Java代码   收藏代码
      1. public void addBook() throws Exception{  
      2.        this.jdbcTemplate.execute(ADD_BOOK);  
      3.        throw new RuntimeException("跳出执行");  
      4.    }  
      Java代码   收藏代码
      1. addUser()方法以下,在方法体中捕获addBook()抛出的异常。若是不捕获异常,addUser()方法将会被终止。  
      Java代码   收藏代码
      1. public void addUser()throws Exception {  
      2.       try {  
      3.           this.bs.addBook();  
      4.       }catch(Exception e) {  
      5.           e.printStackTrace();  
      6.       }  
      7.        this.jdbcTemplate.execute(ADD_USER);  
      8.    }  
      执行junit后,控制台输出以下
    • Java代码   收藏代码
      1. 一、[DEBUG,DataSourceTransactionManager,main] Creating new transaction with name [com.zx.spring.UserService.addUser]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT  
      2. 二、[DEBUG,DataSourceTransactionManager,main] Creating nested transaction with name [com.zx.spring.BookService.addBook]  
      3. 三、[DEBUG,JdbcTemplate,main] Executing SQL statement [insert into t_book(id,name) values(1,'duck-j2ee')]  
      4. 四、[DEBUG,DataSourceTransactionManager,main] Rolling back transaction to savepoint  
      5. 五、[DEBUG,JdbcTemplate,main] Executing SQL statement [insert into t_user(id,name) values(1,'duck')]  
      6. 六、[DEBUG,DataSourceTransactionManager,main] Initiating transaction commit  
      7. 七、[DEBUG,DataSourceTransactionManager,main] Committing JDBC transaction on Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@10e164e]  
         
    • 由上面的输出能够,第一行为执行addUser()方法,事务管理器开始了一个事务,
    • 第二行执行到addBook()方法,因为addBook()方法的事务传播机制为NESTED内嵌事务,所以,开始一个新的事务。
    • 第三行能够知道插入语句,因为addBook()方法内部抛出RuntimeException,所以内部嵌套事务回滚到外层事务建立的保存点。
    • 注意这个地方,咱们抛出的是运行时异常,若是咱们抛出受检查的异常,那么spring会默认的忽略此异常。下面我会详细阐述。
      • 若是内层事务抛出检查异常,那么外层事务将忽略此异常,可是会产生一个问题。那就是:外层事务使用jdbc的保存点API来实现嵌套事务,
      • 可是数据库不必定支持。我作测试的是oracle数据库,jdbc事务管理器在内层事务抛出检查异常后,将会在内层事务结束后,释放外层事务
      • 建立的保存点,这是时候数据库不必定支持。所以可能会抛出以下异常:
      • Java代码   收藏代码
        1. java.sql.SQLException: 不支持的特性  
        2.     at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:112)  
        3.     at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:146)  
        4.     at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:208)  
         
    • 第五行能够知道,外层事务开始执行,第六行可知外层事务提交。
    • 总结可知:对于NESTED内层事务而言,内层事务独立于外层事务,能够独立递交或者回滚。
    • 若是咱们在addUser方法内部抛出一个运行时异常,那么会怎么样呢?
    • Java代码   收藏代码
      1. public void addUser()throws Exception {  
      2.        this.bs.addBook();  
      3.        this.jdbcTemplate.execute(ADD_USER);  
      4.        throw new RuntimeException("throw runtime exception in outter transaction");  
      5.    }  
       
    • Java代码   收藏代码
      1. public void addBook() throws Exception{  
      2.         this.jdbcTemplate.execute(ADD_BOOK);  
      3.     }  
       
    • 执行junit后,控制台输入以下
    • Java代码   收藏代码
      1. 一、[DEBUG,DataSourceTransactionManager,main] Creating new transaction with name [com.zx.spring.UserService.addUser]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT  
      2. 二、[DEBUG,DataSourceTransactionManager,main] Creating nested transaction with name [com.zx.spring.BookService.addBook]  
      3. 三、[DEBUG,JdbcTemplate,main] Executing SQL statement [insert into t_book(id,name) values(1,'duck-j2ee')]  
      4. 四、[DEBUG,DataSourceTransactionManager,main] Releasing transaction savepoint  
      5. 五、[DEBUG,JdbcTemplate,main] Executing SQL statement [insert into t_user(id,name) values(1,'duck')]  
      6. 六、[DEBUG,DataSourceTransactionManager,main] Initiating transaction rollback  
      7. 七、[DEBUG,DataSourceTransactionManager,main] Rolling back JDBC transaction on Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@1622a94]  
       
    • 第一行在addUser()方法执行后,事务管理器建立一个新的事务。
    • 第二上和上面同样,因为addBook()方法是NETSTED的内嵌传播机制,所以新建一个事务。
    • 执行插入,释放保存点。
    • 执行插入t_user插入,可是此时抛出了一个运行时异常,外层事务回滚,那么内层事务是否回滚呢?咱们看如下数据库记录。
    • t_user表数据为空,事务回滚了

       t_book表数据也为空,证实内层事务回滚了


    •  
    • 由上述结果可知,若是对于一个内嵌事务来讲,外层事务的回滚必将致使内层事务回滚。
 
 
  • 五、咱们再将addBook()方法的事务传播机制该为REQUIRES_NEW,来看看会有什么有趣的事情发生?
    • Xml代码   收藏代码
      1. <tx:advice id="txAdvice" transaction-manager="txManager">  
      2.     <tx:attributes>  
      3.     <tx:method name="addUser" propagation="REQUIRED" />  
      4.     <tx:method name="addBook" propagation="REQUIRES_NEW"/>  
      5.     </tx:attributes>  
      6. </tx:advice>  
       
    • Java代码   收藏代码
      1. public void addUser()throws Exception {  
      2.     this.bs.addBook();  
      3.     this.jdbcTemplate.execute(ADD_USER);  
      4. }  
       
    • Java代码   收藏代码
      1. public void addBook() throws Exception{  
      2.     this.jdbcTemplate.execute(ADD_BOOK);  
      3.     //throw new RuntimeException("throw runtime exception in outter transaction");  
      4. }  
       
    • 执行junit后,控制台输出以下
    • Java代码   收藏代码
      1. 一、[DEBUG,DataSourceTransactionManager,main] Creating new transaction with name [com.zx.spring.UserService.addUser]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT  
      2. 二、[DEBUG,DataSourceTransactionManager,main] Suspending current transaction, creating new transaction with name [com.zx.spring.BookService.addBook]  
      3. 三、[DEBUG,JdbcTemplate,main] Executing SQL statement [insert into t_book(id,name) values(1,'duck-j2ee')]  
      4. 四、[DEBUG,DataSourceTransactionManager,main] Initiating transaction commit  
      5. 五、[DEBUG,DataSourceTransactionManager,main] Committing JDBC transaction on Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@5b96c2]  
       
    • 由上可知,第一行执行addUser()方法建立一个事务,
    • 第二行阻塞addUser()方法,并建立一个新的事务,执行插入t_book表,提交内层事务和外层事务。
    • 或许有的读者会问,在下面addUser()方法中因为第一行和第二行是顺序执行,所以不能说明说明问题,那么咱们将addUser()方法中的一、2行代码调换,在看效果:
    • Java代码   收藏代码
      1.   public void addUser()throws Exception {  
      2. 一、        this.bs.addBook();  
      3. 二、       this.jdbcTemplate.execute(ADD_USER);  
      4.     }  
       
    • 兑换后的代码
    • Java代码   收藏代码
      1. public void addUser()throws Exception {  
      2.       this.jdbcTemplate.execute(ADD_USER);  
      3.       this.bs.addBook();  
      4.   }  
       
    • 在来看控制台输出
    • Java代码   收藏代码
      1. 一、[DEBUG,DataSourceTransactionManager,main] Creating new transaction with name [com.zx.spring.UserService.addUser]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT  
      2. 二、[DEBUG,JdbcTemplate,main] Executing SQL statement [insert into t_user(id,name) values(1,'duck')]  
      3.   
      4. 三、[DEBUG,DataSourceTransactionManager,main] Suspending current transaction, creating new transaction with name [com.zx.spring.BookService.addBook]  
      5. 四、[DEBUG,JdbcTemplate,main] Executing SQL statement [insert into t_book(id,name) values(1,'duck-j2ee')]  
      6. 五、[DEBUG,DataSourceTransactionManager,main] Initiating transaction commit  
      7. 六、[DEBUG,DataSourceTransactionManager,main] Committing JDBC transaction on Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@1b7ae22]  
      8.   
      9. 七、[DEBUG,DataSourceTransactionManager,main] Resuming suspended transaction after completion of inner transaction  
      10. 八、[DEBUG,DataSourceTransactionManager,main] Initiating transaction commit  
      11. 九、[DEBUG,DataSourceTransactionManager,main] Committing JDBC transaction on Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@1fa681c]  
       
    • 由第1、二行可知道正在执行插入t_user表操做,而到第3行中咱们能够知道,插入t_user表的事务被挂起,而且新建了一个事务来插入t_book表
    • t_book表插入事务提交后,到第7行可知,前一个事务t_user插入操做被恢复,并提交前一个操做。
  • 若是咱们在addBook()方法中抛出运行时异常,来看看会有什么有趣的事情发生?
    • addBook()方法代码以下
    • Java代码   收藏代码
      1. public void addBook() throws Exception{  
      2.      this.jdbcTemplate.execute(ADD_BOOK);  
      3.      throw new RuntimeException("throw runtime exception in outter transaction");  
      4.  }  
       
    • addUser()方法代码以下
    • Java代码   收藏代码
      1. public void addUser()throws Exception {  
      2.     this.jdbcTemplate.execute(ADD_USER);  
      3.     this.bs.addBook();  
      4. }  
       
    • 执行junit后,控制台输出以下
    • Java代码   收藏代码
      1. 一、[DEBUG,DataSourceTransactionManager,main] Creating new transaction with name [com.zx.spring.UserService.addUser]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT  
      2. 二、[DEBUG,JdbcTemplate,main] Executing SQL statement [insert into t_user(id,name) values(1,'duck')]  
      3. 三、[DEBUG,DataSourceTransactionManager,main] Suspending current transaction, creating new transaction with name [com.zx.spring.BookService.addBook]  
      4. 四、[DEBUG,JdbcTemplate,main] Executing SQL statement [insert into t_book(id,name) values(1,'duck-j2ee')]  
      5. 五、[DEBUG,DataSourceTransactionManager,main] Initiating transaction rollback  
      6. 六、[DEBUG,DataSourceTransactionManager,main] Rolling back JDBC transaction on Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@10d0b72]  
      7. 七、[DEBUG,DataSourceTransactionManager,main] Releasing JDBC Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@10d0b72] after transaction  
      8.   
      9. 八、[DEBUG,DataSourceTransactionManager,main] Resuming suspended transaction after completion of inner transaction八、  
      10. 九、[DEBUG,DataSourceTransactionManager,main] Initiating transaction rollback  
      11. 十、[DEBUG,DataSourceTransactionManager,main] Rolling back JDBC transaction on Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@44b361]  
      12. 十一、[DEBUG,DataSourceTransactionManager,main] Releasing JDBC Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@44b361] after transaction  
       
    • 第一行可知,执行addUser()方法时,从连接池获取一个新连接,建立一个封装事务,执行t_user表插入。
    • 第三行可知,t_user插入事务被挂起,一直到第7行,插入t_book表事务被回滚
    • 第8行可知,t_user事务恢复,可是此时该封装事务被回滚。咱们再看数据库.
    • t_user表数据和t_book表数据均为空


    •  


    •  
    • 由此咱们能够知道,对于REQUIRES_NEW事务传播机制,若是被调用端抛出运行时异常,则被调用端事务回滚,那么调用端的事务究竟是回滚仍是提交呢?
      • 若是调用段代码捕获了被调用端抛出的运行时异常,那么调用端事务提交,不回滚
        • 咱们将addUser()调用端代码该成以下(捕获addBook()抛出的运行时异常)
        • Java代码   收藏代码
          1. public void addUser()throws Exception {  
          2.     this.jdbcTemplate.execute(ADD_USER);  
          3.     try {  
          4.         this.bs.addBook();  
          5.     }catch(Exception e) {  
          6.         e.printStackTrace();  
          7.     }  
          8. }  
           
        • 执行junit后,控制台信息以下
        • Java代码   收藏代码
          1. 一、[DEBUG,DataSourceTransactionManager,main] Creating new transaction with name [com.zx.spring.UserService.addUser]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT  
          2. 二、[DEBUG,DataSourceTransactionManager,main] Acquired Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@1bcec19] for JDBC transaction  
          3. 三、[DEBUG,JdbcTemplate,main] Executing SQL statement [insert into t_user(id,name) values(1,'duck')]  
          4.   
          5.   
          6. 四、[DEBUG,DataSourceTransactionManager,main] Suspending current transaction, creating new transaction with name [com.zx.spring.BookService.addBook]  
          7. 五、[DEBUG,DataSourceTransactionManager,main] Acquired Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@ba507b] for JDBC transaction  
          8. 六、[DEBUG,JdbcTemplate,main] Executing SQL statement [insert into t_book(id,name) values(1,'duck-j2ee')]  
          9. 七、[DEBUG,DataSourceTransactionManager,main] Initiating transaction rollback  
          10. 八、[DEBUG,DataSourceTransactionManager,main] Rolling back JDBC transaction on Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@ba507b]  
          11.   
          12.   
          13. 九、[DEBUG,DataSourceTransactionManager,main] Resuming suspended transaction after completion of inner transaction  
          14. 十、java.lang.RuntimeException: throw runtime exception in outter transaction  
          15.     at com.zx.spring.BookService.addBook(BookService.java:11)  
          16. 十一、[DEBUG,DataSourceTransactionManager,main] Initiating transaction commit  
          17. 十二、[DEBUG,DataSourceTransactionManager,main] Committing JDBC transaction on Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@1bcec19]  
           
        • 由上面的输出能够知道,1-3行从链接池获取一个连接,开始执行插入事务
        • 执行addBook()方法时,因其事务传播属性为REQUIRES_NEW,则将上一个事务阻塞
        • 第6-8行可知,addBook()方法抛出运行时异常,新事务被回滚
        • 第9行恢复执行上一个插入t_user表事务,并捕获到addBook()抛出的异常,自此addUser()方法未抛出任何运行时异常,提交事务。
      • 若是调用端未捕获被调用端抛出的运行时异常,那么调用端事务回滚,不提交
        • 咱们将addUser()方法调用端改为以下(不捕获addBook()抛出的运行时异常,直接抛出)
        • Java代码   收藏代码
          1. public void addUser()throws Exception {  
          2.     this.jdbcTemplate.execute(ADD_USER);  
          3.     this.bs.addBook();  
          4. }  
           
        • 执行junit后,控制台输出以下:
        • Java代码   收藏代码
          1. 一、[DEBUG,DataSourceTransactionManager,main] Creating new transaction with name [com.zx.spring.UserService.addUser]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT  
          2. 二、[DEBUG,JdbcTemplate,main] Executing SQL statement [insert into t_user(id,name) values(1,'duck')]  
          3. 三、[DEBUG,JdbcTemplate,main] Executing SQL statement [insert into t_book(id,name) values(1,'duck-j2ee')]  
          4. 四、[DEBUG,DataSourceTransactionManager,main] Initiating transaction rollback  
          5. 五、[DEBUG,DataSourceTransactionManager,main] Rolling back JDBC transaction on Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@63a721]  
          6.   
          7.   
          8. 六、[DEBUG,DataSourceTransactionManager,main] Resuming suspended transaction after completion of inner transaction  
          9. 七、[DEBUG,DataSourceTransactionManager,main] Initiating transaction rollback  
          10. 八、[DEBUG,DataSourceTransactionManager,main] Rolling back JDBC transaction on Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@1706da8]  
           
        • 由上述第1-5行可知,插入t_user表事务被挂起,同时插入t_book事务被回滚,由于抛出了运行时异常。
        • 6-8行插入t_user事务被回滚,由于addUser()方法的事务传播界别为REQUIRED,所以在抛出了运行时异常的状况下,会回滚事务。
      • 那么,为何会形成上面两种大相径庭的结果呢?由于addUser()方法被声明为REQUIRED传播机制,只要它抛出运行时异常,均会回滚事务。
 
 
  • 六、  咱们再将addBook()方法的事务传播机制该为SUPPORTS,来看看会有什么有趣的事情发生?
    • 将addBook()方法的事务机制改成SUPPORTS
    • Xml代码   收藏代码
      1. <tx:advice id="txAdvice" transaction-manager="txManager">  
      2.     <tx:attributes>  
      3.         <tx:method name="addUser" propagation="REQUIRED" />  
      4.         <tx:method name="addBook" propagation="SUPPORTS"/>  
      5.     </tx:attributes>  
      6. </tx:advice>  
       
    • addUser()方法
    • Java代码   收藏代码
      1. public void addUser()throws Exception {  
      2.       this.bs.addBook();  
      3.       this.jdbcTemplate.execute(ADD_USER);  
      4.   }  
       
    • addBook()方法
    • Java代码   收藏代码
      1. public void addBook() throws Exception{  
      2.        this.jdbcTemplate.execute(ADD_BOOK);  
      3.        throw new RuntimeException("throw runtime exception in outter transaction");  
      4.    }  
       
    • 执行junit后,控制台输出以下
    • Java代码   收藏代码
      1. 一、[DEBUG,DataSourceTransactionManager,main] Creating new transaction with name [com.zx.spring.UserService.addUser]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT  
      2. 二、[DEBUG,DataSourceTransactionManager,main] Participating in existing transaction  
      3. 三、[DEBUG,JdbcTemplate,main] Executing SQL statement [insert into t_book(id,name) values(1,'duck-j2ee')]  
      Java代码   收藏代码
      1. 四、[DEBUG,DataSourceTransactionManager,main] Participating transaction failed - marking existing transaction as rollback-only  
      2. 五、[DEBUG,DataSourceTransactionManager,main] Setting JDBC transaction [com.mchange.v2.c3p0.impl.NewProxyConnection@19e09a4] rollback-only  
      3. 六、[DEBUG,DataSourceTransactionManager,main] Initiating transaction rollback  
      4. 七、[DEBUG,DataSourceTransactionManager,main] Rolling back JDBC transaction on Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@19e09a4]  
       
    • 由第二行可知,addBook()方法的加入到了当前addUser()方法的事务中,第4行可知,addBook()方法抛出运行时异常,此时addUser()方法的事务被标记为rollback,整个事务都
    • 将回滚。若是addUser()方法没有任何事务,那么addBook()方法也不会在事务环境中执行。无论是否抛出异常,sql都将执行。
    • 若是addBook()方法抛出受检查的异常,那么此异常将忽略,整个addUser()方法的事务将提交。addBook()方法也不会尝试去回滚事务
  • 七、  咱们再将addBook()方法的事务传播机制该为NOT_SUPPORTED,会怎么样呢?   
    • 将addBook()方法的事务机制该为NOT_SUPPORTED
    • Xml代码   收藏代码
      1. <tx:advice id="txAdvice" transaction-manager="txManager">  
      2.     <tx:attributes>  
      3.     <tx:method name="addUser" propagation="REQUIRED" />  
      4.     <tx:method name="addBook" propagation="NOT_SUPPORTED"/>  
      5.     </tx:attributes>  
      6. </tx:advice>  
       
    • addBook()方法
    • Java代码   收藏代码
      1. public void addBook() throws Exception{  
      2.       this.jdbcTemplate.execute(ADD_BOOK);  
      3.       throw new RuntimeException("throw runtime exception in outter transaction");  
      4.   }  
       
    • addUser()方法
    • Java代码   收藏代码
      1. public void addUser()throws Exception {  
      2.      this.bs.addBook();  
      3.      this.jdbcTemplate.execute(ADD_USER);  
      4.  }  
       
    • 执行junit后,控制台输出以下
    • Java代码   收藏代码
      1. 一、[DEBUG,DataSourceTransactionManager,main] Creating new transaction with name [com.zx.spring.UserService.addUser]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT  
      2. 二、[DEBUG,DataSourceTransactionManager,main] Suspending current transaction  
      3. 三、[DEBUG,JdbcTemplate,main] Executing SQL statement [insert into t_book(id,name) values(1,'duck-j2ee')]  
      4. 四、[DEBUG,DataSourceUtils,main] Fetching JDBC Connection from DataSource  
      5. 五、[DEBUG,DataSourceTransactionManager,main] Should roll back transaction but cannot - no transaction available  
      6.   
      7.   
      8. 六、[DEBUG,DataSourceTransactionManager,main] Resuming suspended transaction after completion of inner transaction  
      9. 七、[DEBUG,DataSourceTransactionManager,main] Initiating transaction rollback  
      10. 八、[DEBUG,DataSourceTransactionManager,main] Rolling back JDBC transaction on Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@137d4a4]  
       
    • 由上可知,第二行是将addUser()方法的事务挂起,开始执行addBook()代码,
    • 第5行可知,addBook()方法抛出运行时异常,须要回滚事务,可是又没有事务来回滚,所以t_book表数据被插入
    • 因为addBook()抛出异常,在addUser()方法中未捕获该异常,所以对addUser()方法的事务传播机制REQUIRED来讲,抛出了运行时异常,addUser()方法回滚
    • 若是将addUser()方法该为以下:
    • Java代码   收藏代码
      1. public void addUser()throws Exception {  
      2.     try {  
      3.         this.bs.addBook();  
      4.     }catch(Exception e) {  
      5.         e.printStackTrace();  
      6.     }  
      7.     this.jdbcTemplate.execute(ADD_USER);  
      8. }  
       
    • 那么addUser()方法将会提交,addBook()方法将插入一条数据到t_book表中,
    • 若是addBook()方法抛出了受检查异常,addBook()方法将忽略此异常,不尝试任何事务回滚,一样即便在addUser()方法中不捕获addBook()方法抛出的受检查异常,addUser()方法也会提交事务,而忽略此异常。

今天对spring事务7中传播机制之间的做用进行了一个回顾,这里只涉及了jdbc事务管理器的特性,可能会有各类疏忽,但愿各位读者拍砖。spring

相关文章
相关标签/搜索