天天看點

Spring事務的7種傳播行為一文搞定

1.事務傳播行為

什麼叫事務傳播行為?聽起來挺高端的,其實很簡單。

即然是傳播,那麼至少有兩個東西,才可以發生傳播。單體不存在傳播這個行為。

事務傳播行為(propagation behavior)指的就是當一個事務方法被另一個事務方法調用時,這個事務方法應該如何進行。

例如:methodA事務方法調用methodB事務方法時,methodB是繼續在調用者methodA的事務中運作呢,還是為自己開啟一個新事務運作,這就是由methodB的事務傳播行為決定的。

Spring定義了七種傳播行為:

Spring事務的7種傳播行為一文搞定

2.PROPAGATION_REQUIRED

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

可以把事務想像成一個膠囊,在這個場景下方法B用的是方法A産生的膠囊(事務)。

Spring事務的7種傳播行為一文搞定

舉例有兩個方法:

@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
 methodB();
// do something
}
 
@Transactional(propagation = Propagation.REQUIRED)
public void methodB() {
    // do something
}      

單獨調用methodB方法時,因為目前上下文不存在事務,是以會開啟一個新的事務。

調用methodA方法時,因為目前上下文不存在事務,是以會開啟一個新的事務。當執行到methodB時,methodB發現目前上下文有事務,是以就加入到目前事務中來。

3.PROPAGATION_SUPPORTS

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

舉例有兩個方法:

@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
 methodB();
// do something
}
 
// 事務屬性為SUPPORTS
@Transactional(propagation = Propagation.SUPPORTS)
public void methodB() {
    // do something
}      

單純的調用methodB時,methodB方法是非事務的執行的。當調用methdA時,methodB則加入了methodA的事務中,事務地執行。

4.PROPAGATION_MANDATORY

如果已經存在一個事務,支援目前事務。如果沒有一個活動的事務,則抛出異常。

@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
 methodB();
// do something
}
 
// 事務屬性為MANDATORY
@Transactional(propagation = Propagation.MANDATORY)
public void methodB() {
    // do something
}      

當單獨調用methodB時,因為目前沒有一個活動的事務,則會抛出異常throw new IllegalTransactionStateException(“Transaction propagation ‘mandatory’ but no existing transaction found”);當調用methodA時,methodB則加入到methodA的事務中,事務地執行。

5.PROPAGATION_REQUIRES_NEW

Spring事務的7種傳播行為一文搞定

使用PROPAGATION_REQUIRES_NEW,需要使用 JtaTransactionManager作為事務管理器。

它會開啟一個新的事務。如果一個事務已經存在,則先将這個存在的事務挂起。

@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
doSomeThingA();
methodB();
doSomeThingB();
// do something else
}
 
 
// 事務屬性為REQUIRES_NEW
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void methodB() {
    // do something
}      

當調用

main{  
methodA();
}      

相當于調用

main(){
    TransactionManager tm = null;
    try{
        //獲得一個JTA事務管理器
        tm = getTransactionManager();
        tm.begin();//開啟一個新的事務
        Transaction ts1 = tm.getTransaction();
        doSomeThing();
        tm.suspend();//挂起目前事務
        try{
            tm.begin();//重新開啟第二個事務
            Transaction ts2 = tm.getTransaction();
            methodB();
            ts2.commit();//送出第二個事務
        } Catch(RunTimeException ex) {
            ts2.rollback();//復原第二個事務
        } finally {
            //釋放資源
        }
        //methodB執行完後,恢複第一個事務
        tm.resume(ts1);
        doSomeThingB();
        ts1.commit();//送出第一個事務
    } catch(RunTimeException ex) {
        ts1.rollback();//復原第一個事務
    } finally {
        //釋放資源
    }
}      

在這裡,我把ts1稱為外層事務,ts2稱為内層事務。從上面的代碼可以看出,ts2與ts1是兩個獨立的事務,互不相幹。Ts2是否成功并不依賴于 ts1。如果methodA方法在調用methodB方法後的doSomeThingB方法失敗了,而methodB方法所做的結果依然被送出。而除了 methodB之外的其它代碼導緻的結果卻被復原了.

6.PROPAGATION_NOT_SUPPORTED

PROPAGATION_NOT_SUPPORTED 總是非事務地執行,并挂起任何存在的事務。使用PROPAGATION_NOT_SUPPORTED,也需要使用JtaTransactionManager作為事務管理器。

Spring事務的7種傳播行為一文搞定

7.PROPAGATION_NEVER

總是非事務地執行,如果存在一個活動事務,則抛出異常。

8.PROPAGATION_NESTED

Spring事務的7種傳播行為一文搞定

如果一個活動的事務存在,則運作在一個嵌套的事務中。 如果沒有活動事務, 則按TransactionDefinition.PROPAGATION_REQUIRED 屬性執行。

這是一個嵌套事務,使用JDBC 3.0驅動時,僅僅支援DataSourceTransactionManager作為事務管理器。

需要JDBC 驅動的java.sql.Savepoint類。使用PROPAGATION_NESTED,還需要把PlatformTransactionManager的nestedTransactionAllowed屬性設為true(屬性值預設為false)。

這裡關鍵是嵌套執行。

@Transactional(propagation = Propagation.REQUIRED)
methodA(){
  doSomeThingA();
  methodB();
  doSomeThingB();
}
 
@Transactional(propagation = Propagation.NEWSTED)
methodB(){
  ……
}      

如果單獨調用methodB方法,則按REQUIRED屬性執行。如果調用methodA方法,相當于下面的效果:

main(){
    Connection con = null;
    Savepoint savepoint = null;
    try{
        con = getConnection();
        con.setAutoCommit(false);
        doSomeThingA();
        savepoint = con2.setSavepoint();
        try{
            methodB();
        } catch(RuntimeException ex) {
            con.rollback(savepoint);
        } finally {
            //釋放資源
        }
        doSomeThingB();
        con.commit();
    } catch(RuntimeException ex) {
        con.rollback();
    } finally {
        //釋放資源
    }
}      

當methodB方法調用之前,調用setSavepoint方法,儲存目前的狀态到savepoint。如果methodB方法調用失敗,則恢複到之前儲存的狀态。但是需要注意的是,這時的事務并沒有進行送出,如果後續的代碼(doSomeThingB()方法)調用失敗,則復原包括methodB方法的所有操作。嵌套事務一個非常重要的概念就是内層事務依賴于外層事務。外層事務失敗時,會復原内層事務所做的動作。而内層事務操作失敗并不會引起外層事務的復原。

9.PROPAGATION_NESTED 與PROPAGATION_REQUIRES_NEW的差別

它們非常類似,都像一個嵌套事務,如果不存在一個活動的事務,都會開啟一個新的事務。

使用 PROPAGATION_REQUIRES_NEW時,内層事務與外層事務就像兩個獨立的事務一樣,一旦内層事務進行了送出後,外層事務不能對其進行復原。兩個事務互不影響。兩個事務不是一個真正的嵌套事務。同時它需要JTA事務管理器的支援。

使用PROPAGATION_NESTED時,外層事務的復原可以引起内層事務的復原。而内層事務的異常并不會導緻外層事務的復原,它是一個真正的嵌套事務。DataSourceTransactionManager使用savepoint支援PROPAGATION_NESTED時,需要JDBC 3.0以上驅動及1.4以上的JDK版本支援。其它的JTATrasactionManager實作可能有不同的支援方式。

PROPAGATION_REQUIRES_NEW 啟動一個新的, 不依賴于環境的 “内部” 事務. 這個事務将被完全 commited 或 rolled back 而不依賴于外部事務, 它擁有自己的隔離範圍, 自己的鎖, 等等. 當内部事務開始執行時, 外部事務将被挂起, 内務事務結束時, 外部事務将繼續執行。

繼續閱讀