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

相關文章
相關標籤/搜索