1. 程式人生 > >事務457——事務的七個傳播行為

事務457——事務的七個傳播行為

繼上一篇文章,我們講到了事務的傳播行為,具體是那七個。我們在羅列一遍:

  • PROPAGATION_REQUIRED

    • 如果存在一個事務,則支援當前事務。如果沒有事務則開啟一個新的事務。
  • PROPAGATION_SUPPORTS

    • 如果存在一個事務,支援當前事務。如果沒有事務,則非事務的執行。但是對於事務同步的事務管理器,PROPAGATION_SUPPORTS與不使用事務有少許不同。
  • PROPAGATION_MANDATORY

    • 如果已經存在一個事務,支援當前事務。如果沒有一個活動的事務,則丟擲異常。
  • PROPAGATION_REQUIRES_NEW

    • 總是開啟一個新的事務。如果一個事務已經存在,則將這個存在的事務掛起。
  • PROPAGATION_NOT_SUPPORTED

    • 總是非事務地執行,並掛起任何存在的事務
  • PROPAGATION_NEVER

  • 總是非事務地執行,如果存在一個活動事務,則丟擲異常

  • PROPAGATION_NESTED

    • 如果一個活動的事務存在,則執行在一個巢狀的事務中. 如果沒有活動事務, 則按TransactionDefinition.PROPAGATION_REQUIRED 屬性執行

我們就轉賬例子說一下。 比如,在轉賬的services中有個兩個方法,方法A和方法B。

方法A
 ServiceA { 
        read A =100  ;   
        If
(doSome()){ A = A + 1; }else{ A = A - 1; } Update A; Commit; }
方法B
ServiceB {                 
         void doOther() {
                 read B ;
                 B =
B – 1; Update B; If(B>0){ Commit; Return ture; }else{ Rollback; Return false; } }

(1)REQUIRED——必須有事務

指定的方法必須在事務內執行。若當前存在事務,就加入到當前事務中;若當前沒有事務,則建立一個新事務。這種傳播行為是最常見的選擇,也是Spring預設的事務傳播行為。

比如說,Servicedo的doOther()方法上加上了傳播行為PROPAGATION_REQUIRED。若doSome()方法在呼叫doOther()方法時就是在事務內執行的,則doOther()方法的執行也加入到該事務內執行。若doSome()方法在呼叫doOther()方法時沒有在事務內執行,則doOther()方法會促使doSome()建立一個事務,並在加入其中執行。在doSome()或者在doOther()內的任何地方出現異常,事務都會被回滾。即使doOther()的事務已經被提交,doSome()在接下來fail要回滾,doOther()也要回滾。

簡單來說,誰調加了REQUIRED的方法,誰就要具有一個事務,被呼叫的方法可有可無事務,但是調他的必須有事務
這裡寫圖片描述

(2)SUPPORTS ——支援事務

指定的方法支援當前事務,但若當前沒有事務,也可以以非事務方式執行。 定義在再方法doOther()上,定義在doSome()上。

這裡寫圖片描述

(3)MANDATORY ——託管事務

指定的方法必須在當前事務內執行。就是說,他只能被一個父事務呼叫。若當前沒有事務,則直接丟擲異常。 定義在再方法doOther()上,定義在doSome()上。doSome()是father。

這裡寫圖片描述

(4)REQUIRES_NEW ——需要新事務

總是新建一個事務,若當前存在事務,就將當前事務掛起,直到新事務執行完畢。

比如,我們設計ServiceA.doSome()的事務級別為PROPAGATION_REQUIRED,ServiceB.doOther()的事務級別為PROPAGATION_REQUIRES_NEW,那麼當執行到ServiceB.doOther()的時候,ServiceA.doSome()所在的事務就會掛起,ServiceB.doOther()會起一個新的事務,等待ServiceB.doOther()的事務完成以後,他才繼續執行。

優先級別:PROPAGATION_REQUIRES_NEW >PROPAGATION_REQUIRES

這裡寫圖片描述

PROPAGATION_REQUIRED與PROPAGATION_REQUIRED 的事務區別:在於事務的回滾程度。因為ServiceB.doOther()是新起一個事務,那麼就是存在兩個不同的事務。如果ServiceB.doOther()已經提交,那麼ServiceA.doSome()失敗回滾,ServiceB.doOther()是不會回滾的。如果ServiceB.doOther()失敗回滾,如果他丟擲的異常被ServiceA.doSome()捕獲,ServiceA.doSome()事務仍然可能提交。

(5)NOT_SUPPORTED ——不支援事務

指定的方法不能在事務環境中執行,若當前存在事務,就將當前事務掛起。
比如ServiceA.doSome()的事務級別是PROPAGATION_REQUIRED ,而ServiceB.doOther()的事務級別是PROPAGATION_NOT_SUPPORTED ,那麼當執行到ServiceB.doOther()時,ServiceA.doSome()的事務掛起,而他以非事務的狀態執行完,再繼續ServiceA.doSome()的事務。一般,在非常耗時查詢的時候就可以使用Not Support,掛起這個查詢方法,讓其他方法先行,避免事務時間超長。

優先級別:PROPAGATION_NOT_SUPPORTED>PROPAGATION_REQUIRED

這裡寫圖片描述

(6)NEVER ——禁用事務

指定的方法不能在事務環境下執行,若當前存在事務,就直接丟擲異常。
假設ServiceA.doSome()的事務級別是PROPAGATION_REQUIRED,而ServiceB.doOther()的事務級別是PROPAGATION_NEVER ,那麼ServiceB.doOther()就要丟擲異常了。
簡而言之:事務掉了擁有NEVER屬性的方法統統要出錯。
優先級別:PROPAGATION_NEVER>PROPAGATION_REQUIRED

這裡寫圖片描述

(7)NESTED ——巢狀事務

指定的方法必須在事務內執行。若當前存在事務,則在巢狀事務內執行;若當前沒有事務,則建立一個新事務。並且使用savepoint設定事務的回滾點。
這裡寫圖片描述

那怎麼使用savapoint()的,看下面這個例子

savapoint()使用
ServiceA {        
     void doSome() {   
         try {
      //savepoint   
             ServiceB.doOther();    //PROPAGATION_NESTED 級別
         } catch (SomeException) {   
             // 執行其他業務, 如 ServiceC.doElse()等等
         }   
     }   
  }   

加入,ServiceB.doOther()失敗回滾,那麼ServiceA.doSome() 也會回滾到savepoint點上,ServiceA.doSome() 可以選擇另外一個分支,比如ServiceC.doElse(),繼續執行,來嘗試完成自己的事務。

PROPAGATION_Nested與PROPAGATION_REQUIRES_NEW的區別是:PROPAGATION_REQUIRES_NEW另起一個事務,將會與他的父事務相互獨立,而Nested的事務和他的父事務是相依的,他的提交是要等和他的父事務一塊提交的。也就是說,如果父事務最後回滾,他也要回滾的。