Spring事務的傳播:PROPAGATION_REQUIRED


PROPAGATION_REQUIRED-- 支持當前事務,如果當前沒有事務,就新建一個事務。這是最常見的選擇。 

 

  1. ServiceA {           
  2.      void methodA() {  
  3.          ServiceB.methodB();  
  4.      }  
  5. }      
  6. ServiceB {           
  7.      void methodB() {  
  8.      }           
  9. }  

PROPAGATION_REQUIRED

假如當前正要執行的事務不在另外一個事務里,那么就起一個新的事務 
比如說,ServiceB.methodB的事務級別定義為PROPAGATION_REQUIRED, 那么由於執行ServiceA.methodA的時候
  1、如果ServiceA.methodA已經起了事務,這時調用ServiceB.methodB,ServiceB.methodB看到自己已經運行在ServiceA.methodA的事務內部,就不再起新的事務。這時只有外部事務並且他們是共用的,所以這時ServiceA.methodA或者ServiceB.methodB無論哪個發生異常methodA和methodB作為一個整體都將一起回滾。
  2、如果ServiceA.methodA沒有事務,ServiceB.methodB就會為自己分配一個事務。這樣,在ServiceA.methodA中是沒有事務控制的。只是在ServiceB.methodB內的任何地方出現異常,ServiceB.methodB將會被回滾,不會引起ServiceA.methodA的回滾
-----------------以上為重點內容----------------以下為引用全文------------

 

 

事務的嵌套概念

所謂事務的嵌套就是兩個事務方法之間相互調用。spring事務開啟 ,或者是基於接口的或者是基於類的代理被創建(注意一定要是代理,不能手動new 一個對象,並且此類(有無接口都行)一定要被代理——spring中的bean只要納入了IOC管理都是被代理的)。所以在同一個類中一個方法調用另一個方法有事務的方法,事務是不會起作用的。
 
###
Spring默認情況下會對運行期例外(RunTimeException),即uncheck異常,進行事務回滾。
如果遇到checked異常就不回滾。
如何改變默認規則:

1 讓checked例外也回滾:在整個方法前加上 @Transactional(rollbackFor=Exception.class)


2 讓unchecked例外不回滾: @Transactional(notRollbackFor=RunTimeException.class)


3 不需要事務管理的(只查詢的)方法:@Transactional(propagation=Propagation.NOT_SUPPORTED)
上面三種方式也可在xml配置
 

spring事務傳播屬性

 在 spring的 TransactionDefinition接口中一共定義了六種事務傳播屬性:
 
PROPAGATION_REQUIRED -- 支持當前事務,如果當前沒有事務,就新建一個事務。這是最常見的選擇。 
PROPAGATION_SUPPORTS -- 支持當前事務,如果當前沒有事務,就以非事務方式執行。 
PROPAGATION_MANDATORY -- 支持當前事務,如果當前沒有事務,就拋出異常。 
PROPAGATION_REQUIRES_NEW -- 新建事務,如果當前存在事務,把當前事務掛起。 
PROPAGATION_NOT_SUPPORTED -- 以非事務方式執行操作,如果當前存在事務,就把當前事務掛起。 
PROPAGATION_NEVER -- 以非事務方式執行,如果當前存在事務,則拋出異常。 
PROPAGATION_NESTED -- 如果當前存在事務,則在嵌套事務內執行。如果當前沒有事務,則進行與PROPAGATION_REQUIRED類似的操作。 
前六個策略類似於EJB CMT,第七個(PROPAGATION_NESTED)是Spring所提供的一個特殊變量。 
它要求事務管理器或者使用JDBC 3.0 Savepoint API提供嵌套事務行為(如Spring的DataSourceTransactionManager) 
 
 
舉例淺析Spring嵌套事務
ServiceA#methodA(我們稱之為外部事務),ServiceB#methodB(我們稱之為外部事務)
 
[java]  view plain  copy
 
 
 
 
  1. ServiceA {  
  2.          
  3.      void methodA() {  
  4.          ServiceB.methodB();  
  5.      }  
  6.     
  7. }  
  8.     
  9. ServiceB {  
  10.          
  11.      void methodB() {  
  12.      }  
  13.          
  14. }  
 

PROPAGATION_REQUIRED

假如當前正要執行的事務不在另外一個事務里,那么就起一個新的事務 
比如說,ServiceB.methodB的事務級別定義為PROPAGATION_REQUIRED, 那么由於執行ServiceA.methodA的時候
  1、如果ServiceA.methodA已經起了事務,這時調用ServiceB.methodB,ServiceB.methodB看到自己已經運行在ServiceA.methodA的事務內部,就不再起新的事務。這時只有外部事務並且他們是共用的,所以這時ServiceA.methodA或者ServiceB.methodB無論哪個發生異常methodA和methodB作為一個整體都將一起回滾。
  2、如果ServiceA.methodA沒有事務,ServiceB.methodB就會為自己分配一個事務。這樣,在ServiceA.methodA中是沒有事務控制的。只是在ServiceB.methodB內的任何地方出現異常,ServiceB.methodB將會被回滾,不會引起ServiceA.methodA的回滾
 

PROPAGATION_SUPPORTS

如果當前在事務中,即以事務的形式運行,如果當前不再一個事務中,那么就以非事務的形式運行 

PROPAGATION_MANDATORY

必須在一個事務中運行。也就是說,他只能被一個父事務調用。否則,他就要拋出異常

PROPAGATION_REQUIRES_NEW

啟動一個新的, 不依賴於環境的 "內部" 事務. 這個事務將被完全 commited 或 rolled back 而不依賴於外部事務, 它擁有自己的隔離范圍, 自己的鎖, 等等. 當內部事務開始執行時, 外部事務將被掛起, 內務事務結束時, 外部事務將繼續執行. 
 比如我們設計ServiceA.methodA的事務級別為PROPAGATION_REQUIRED,ServiceB.methodB的事務級別為PROPAGATION_REQUIRES_NEW,那么當執行到ServiceB.methodB的時候,ServiceA.methodA所在的事務就會掛起,ServiceB.methodB會起一個新的事務,等待ServiceB.methodB的事務完成以后,他才繼續執行。他與PROPAGATION_REQUIRED 的事務區別在於事務的回滾程度了。因為ServiceB.methodB是新起一個事務,那么就是存在兩個不同的事務。
1、如果ServiceB.methodB已經提交,那么ServiceA.methodA失敗回滾,ServiceB.methodB是不會回滾的。
2、如果ServiceB.methodB失敗回滾,如果他拋出的異常被ServiceA.methodA的try..catch捕獲並處理,ServiceA.methodA事務仍然可能提交;如果他拋出的異常未被ServiceA.methodA捕獲處理,ServiceA.methodA事務將回滾。
 
使用場景:
不管業務邏輯的service是否有異常,Log Service都應該能夠記錄成功,所以Log Service的傳播屬性可以配為此屬性。最下面將會貼出配置代碼。
 

PROPAGATION_NOT_SUPPORTED

當前不支持事務。比如ServiceA.methodA的事務級別是PROPAGATION_REQUIRED ,而ServiceB.methodB的事務級別是PROPAGATION_NOT_SUPPORTED ,那么當執行到ServiceB.methodB時,ServiceA.methodA的事務掛起,而他以非事務的狀態運行完,再繼續ServiceA.methodA的事務。

PROPAGATION_NEVER

不能在事務中運行。假設ServiceA.methodA的事務級別是PROPAGATION_REQUIRED, 而ServiceB.methodB的事務級別是PROPAGATION_NEVER ,那么ServiceB.methodB就要拋出異常了。 

PROPAGATION_NESTED

開始一個 "嵌套的" 事務,  它是已經存在事務的一個真正的子事務. 潛套事務開始執行時,  它將取得一個 savepoint. 如果這個嵌套事務失敗, 我們將回滾到此 savepoint. 潛套事務是外部事務的一部分, 只有外部事務結束后它才會被提交. 
 
比如我們設計ServiceA.methodA的事務級別為PROPAGATION_REQUIRED,ServiceB.methodB的事務級別為PROPAGATION_NESTED,那么當執行到ServiceB.methodB的時候,ServiceA.methodA所在的事務就會掛起,ServiceB.methodB會起一個新的子事務並設置savepoint,等待ServiceB.methodB的事務完成以后,他才繼續執行。。因為ServiceB.methodB是外部事務的子事務,那么
1、如果ServiceB.methodB已經提交,那么ServiceA.methodA失敗回滾,ServiceB.methodB也將回滾。
2、如果ServiceB.methodB失敗回滾,如果他拋出的異常被ServiceA.methodA的try..catch捕獲並處理,ServiceA.methodA事務仍然可能提交;如果他拋出的異常未被ServiceA.methodA捕獲處理,ServiceA.methodA事務將回滾。
理解Nested的關鍵是savepoint。他與PROPAGATION_REQUIRES_NEW的區別是:
PROPAGATION_REQUIRES_NEW 完全是一個新的事務,它與外部事務相互獨立; 而 PROPAGATION_NESTED 則是外部事務的子事務, 如果外部事務 commit, 嵌套事務也會被 commit, 這個規則同樣適用於 roll back. 
 

在 spring 中使用 PROPAGATION_NESTED的前提:

1. 我們要設置 transactionManager 的 nestedTransactionAllowed 屬性為 true, 注意, 此屬性默認為 false!!! 

2. java.sql.Savepoint 必須存在, 即 jdk 版本要 1.4+ 

3. Connection.getMetaData().supportsSavepoints() 必須為 true, 即 jdbc drive 必須支持 JDBC 3.0 


確保以上條件都滿足后, 你就可以嘗試使用 PROPAGATION_NESTED 了. 
 
 
##############################################################################

Log Service配置事務傳播

不管業務邏輯的service是否有異常,Log Service都應該能夠記錄成功,通常有異常的調用更是用戶關心的。Log Service如果沿用業務邏輯Service的事務的話在拋出異常時將沒有辦法記錄日志(事實上是回滾了)。所以希望Log Service能夠有獨立的事務。日志和普通的服務應該具有不同的策略。Spring 配置文件transaction.xml:
[html]  view plain  copy
 
 
 
 
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <beans xmlns="http://www.springframework.org/schema/beans"  
  3.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"  
  4.     xmlns:tx="http://www.springframework.org/schema/tx"  
  5.     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">  
  6.     <!-- configure transaction -->  
  7.   
  8.     <tx:advice id="defaultTxAdvice" transaction-manager="transactionManager">  
  9.         <tx:attributes>  
  10.             <tx:method name="get*" read-only="true" />  
  11.             <tx:method name="query*" read-only="true" />  
  12.             <tx:method name="find*" read-only="true" />  
  13.             <tx:method name="*" propagation="REQUIRED" rollback-for="java.lang.Exception" />  
  14.         </tx:attributes>  
  15.     </tx:advice>  
  16.   
  17.     <tx:advice id="logTxAdvice" transaction-manager="transactionManager">  
  18.         <tx:attributes>  
  19.             <tx:method name="get*" read-only="true" />  
  20.             <tx:method name="query*" read-only="true" />  
  21.             <tx:method name="find*" read-only="true" />  
  22.             <tx:method name="*" propagation="REQUIRES_NEW"  
  23.                 rollback-for="java.lang.Exception" />  
  24.         </tx:attributes>  
  25.     </tx:advice>  
  26.   
  27.     <aop:config>  
  28.         <aop:pointcut id="defaultOperation"  
  29.             expression="@within(com.homent.util.DefaultTransaction)" />  
  30.         <aop:pointcut id="logServiceOperation"  
  31.             expression="execution(* com.homent.service.LogService.*(..))" />  
  32.               
  33.         <aop:advisor advice-ref="defaultTxAdvice" pointcut-ref="defaultOperation" />  
  34.         <aop:advisor advice-ref="logTxAdvice" pointcut-ref="logServiceOperation" />  
  35.     </aop:config>  
  36. </beans>  
 
 如上面的Spring配置文件所示,日志服務的事務策略配置為propagation="REQUIRES_NEW",告訴Spring不管上下文是否有事務,Log Service被調用時都要求一個完全新的只屬於Log Service自己的事務。通過該事務策略,Log Service可以獨立的記錄日志信息,不再受到業務邏輯事務的干擾。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM