Spring事務傳播行爲---醍醐灌頂

這篇文章非常好,非常容易的理解事務傳播行爲,因此厚顏無恥的轉到這裏來了,收藏一下。

 

事務傳播行爲

什麼叫事務傳播行爲?聽起來挺高端的,其實很簡單。 
即然是傳播,那麼至少有兩個東西,纔可以發生傳播。單體不存在傳播這個行爲。

事務傳播行爲(propagation behavior)指的就是當一個事務方法被另一個事務方法調用時,這個事務方法應該如何進行。 
例如:methodA事務方法調用methodB事務方法時,methodB是繼續在調用者methodA的事務中運行呢,還是爲自己開啓一個新事務運行,這就是由methodB的事務傳播行爲決定的。

Spring定義了七種傳播行爲:

這裏寫圖片描述

現在來看看傳播行爲

1、PROPAGATION_REQUIRED

總結: A、B事務同生共死,相互依賴

如果存在一個事務,則支持當前事務。如果沒有事務則開啓一個新的事務。 
可以把事務想像成一個膠囊,在這個場景下方法B用的是方法A產生的膠囊(事務)。 
這裏寫圖片描述

舉例有兩個方法:

  1.  

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

     

單獨調用methodB方法時,因爲當前上下文不存在事務,所以會開啓一個新的事務。 
調用methodA方法時,因爲當前上下文不存在事務,所以會開啓一個新的事務。當執行到methodB時,methodB發現當前上下文有事務,因此就加入到當前事務中來。

2、PROPAGATION_SUPPORTS

總結:有則支持,無責隨意。

如果存在一個事務,支持當前事務。如果沒有事務,則非事務的執行。但是對於事務同步的事務管理器,PROPAGATION_SUPPORTS與不使用事務有少許不同。 
舉例有兩個方法:

  1.  

    @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的事務中,事務地執行。

3、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的事務中,事務地執行。

4、PROPAGATION_REQUIRES_NEW

總結:翅膀硬了,管不了了。

 

使用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之外的其它代碼導致的結果卻被回滾了

5、PROPAGATION_NOT_SUPPORTED

總結:我不支持,任你如何。

PROPAGATION_NOT_SUPPORTED 總是非事務地執行,並掛起任何存在的事務。使用PROPAGATION_NOT_SUPPORTED,也需要使用JtaTransactionManager作爲事務管理器。 
這裏寫圖片描述

6、PROPAGATION_NEVER

總結:必須有事務

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

7、PROPAGATION_NESTED

總結:寄生關係,君死臣不獨存。

這裏寫圖片描述 
如果一個活動的事務存在,則運行在一個嵌套的事務中。 如果沒有活動事務, 則按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方法的所有操作。嵌套事務一個非常重要的概念就是內層事務依賴於外層事務。外層事務失敗時,會回滾內層事務所做的動作。而內層事務操作失敗並不會引起外層事務的回滾。

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 而不依賴於外部事務, 它擁有自己的隔離範圍, 自己的鎖, 等等. 當內部事務開始執行時, 外部事務將被掛起, 內務事務結束時, 外部事務將繼續執行。

另一方面, PROPAGATION_NESTED 開始一個 “嵌套的” 事務, 它是已經存在事務的一個真正的子事務. 潛套事務開始執行時, 它將取得一個 savepoint. 如果這個嵌套事務失敗, 我們將回滾到此 savepoint. 潛套事務是外部事務的一部分, 只有外部事務結束後它纔會被提交。

由此可見, PROPAGATION_REQUIRES_NEW 和 PROPAGATION_NESTED 的最大區別在於, PROPAGATION_REQUIRES_NEW 完全是一個新的事務, 而 PROPAGATION_NESTED 則是外部事務的子事務, 如果外部事務 commit, 嵌套事務也會被 commit, 這個規則同樣適用於 roll back.

 

注: 實際中,AB方法之間的調用,需要用service調用,或者兩者不在同一類中,否則直接調用方法註解不生效。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章