天天看點

Spring - 事務之傳播行為

事務的第一個方面是傳播行為(propagation behavior)。當事務方法被另一個事務方法調用時,必須指定事務應該如何傳播。例如:方法可能繼續在現有事務中運作,也可能開啟一個新事務,并在自己的事務中運作。Spring定義了七種傳播行為:
Spring - 事務之傳播行為

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

//事務屬性 PROPAGATION_REQUIRED
methodA{
    ……
    methodB();
    ……
}      
//事務屬性 PROPAGATION_REQUIRED
methodB{
   ……
}      

使用Spring聲明式事務,Spring使用AOP來支援聲明式事務,會根據事務屬性,自動在方法調用之前決定是否開啟一個事務,并在方法執行之後決定事務送出或復原事務。

單獨調用methodB方法:

main{ 
    methodB(); 
}      

相當于

Main{ 
    Connection con=null; 
    try{ 
        con = getConnection(); 
        con.setAutoCommit(false); 

        //方法調用
        methodB(); 

        //送出事務
        con.commit(); 
    } Catch(RuntimeException ex) { 
        //復原事務
        con.rollback();   
    } finally { 
        //釋放資源
        closeCon(); 
    } 
}      

Spring保證在methodB方法中所有的調用都獲得到一個相同的連接配接。在調用methodB時,沒有一個存在的事務,是以獲得一個新的連接配接,開啟了一個新的事務。 

單獨調用MethodA時,在MethodA内又會調用MethodB。

執行效果相當于:

main{ 
    Connection con = null; 
    try{ 
        con = getConnection(); 
        methodA(); 
        con.commit(); 
    } catch(RuntimeException ex) { 
        con.rollback(); 
    } finally {    
        closeCon(); 
    }  
}      

調用MethodA時,環境中沒有事務,是以開啟一個新的事務。當在MethodA中調用MethodB時,環境中已經有了一個事務,是以methodB就加入目前事務。

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

//事務屬性 PROPAGATION_REQUIRED
methodA(){
  methodB();
}

//事務屬性 PROPAGATION_SUPPORTS
methodB(){
  ……
}      

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

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

//事務屬性 PROPAGATION_REQUIRED
methodA(){
    methodB();
}

//事務屬性 PROPAGATION_MANDATORY
    methodB(){
    ……
}      

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

(4)PROPAGATION_REQUIRES_NEW 總是開啟一個新的事務。如果一個事務已經存在,則将這個存在的事務挂起。

//事務屬性 PROPAGATION_REQUIRED
methodA(){
    doSomeThingA();
    methodB();
    doSomeThingB();
}

//事務屬性 PROPAGATION_REQUIRES_NEW
methodB(){
    ……
}      

調用A方法:

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 之外的其它代碼導緻的結果卻被復原了。使用PROPAGATION_REQUIRES_NEW,需要使用 JtaTransactionManager作為事務管理器。

(5)PROPAGATION_NOT_SUPPORTED 總是非事務地執行,并挂起任何存在的事務。使用PROPAGATION_NOT_SUPPORTED,也需要使用JtaTransactionManager作為事務管理器。(代碼示例同上,可同理推出)

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

(7)PROPAGATION_NESTED如果一個活動的事務存在,則運作在一個嵌套的事務中。如果沒有活動事務,則按TransactionDefinition.PROPAGATION_REQUIRED 屬性執行。這是一個嵌套事務,使用JDBC 3.0驅動時,僅僅支援DataSourceTransactionManager作為事務管理器。需要JDBC 驅動的java.sql.Savepoint類。有一些JTA的事務管理器實作可能也提供了同樣的功能。使用PROPAGATION_NESTED,還需要把PlatformTransactionManager的nestedTransactionAllowed屬性設為true;而 nestedTransactionAllowed屬性值預設為false。

//事務屬性 PROPAGATION_REQUIRED
methodA(){
    doSomeThingA();
    methodB();
    doSomeThingB();
}

//事務屬性 PROPAGATION_NESTED
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方法的所有操作。

嵌套事務一個非常重要的概念就是内層事務依賴于外層事務。外層事務失敗時,會復原内層事務所做的動作。而内層事務操作失敗并不會引起外層事務的復原。

PROPAGATION_NESTED 與PROPAGATION_REQUIRES_NEW的差別:它們非常類似,都像一個嵌套事務,如果不存在一個活動的事務,都會開啟一個新的事務。使用 PROPAGATION_REQUIRES_NEW時,内層事務與外層事務就像兩個獨立的事務一樣,一旦内層事務進行了送出後,外層事務不能對其進行復原。兩個事務互不影響。兩個事務不是一個真正的嵌套事務。同時它需要JTA事務管理器的支援。

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

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

另一方面,PROPAGATION_NESTED 開始一個 “嵌套的” 事務,它是已經存在事務的一個真正的子事務。潛套事務開始執行時,它将取得一個 savepoint 如果這個嵌套事務失敗,我們将復原到此 savepoint 潛套事務是外部事務的一部分,隻有外部事務結束後它才會被送出。

繼續閱讀