說說Spring事務的傳播機制

寫在前面

琪姐問了個問題,原來的方法邏輯,相似以下僞代碼:數據庫

updateTableA.setStatus(0);
rpcOperateAccount();
updateTableA.setStatus(1);

相信很好理解。優化

琪姐說將這段邏輯放到線程池中一塊兒提交就OK(意思應該是DB落庫成功)。ui

executorService.execute(()=>{
	try{
		// 上面那段邏輯
	} catch(Exception e){
		LOGGER.error("")
	}
})

可是把那段邏輯拿出來就落庫失敗:線程

func doSomething() throw BizException	{
	// 上面那段邏輯
}

最後琪姐說,修改了Spring的事務傳播機制好了(應該是從默認的Required => Requires_New)。code

雖然沒有源碼,可是能夠基於這個問題說說Spring的事務傳播機制。接口

並且事務會由於RuntimeException和Error回滾。事務

Spring的傳播機制

傳播記住有以下幾種:rpc

public interface TransactionDefinition {
    int PROPAGATION_REQUIRED = 0;
    int PROPAGATION_SUPPORTS = 1;
    int PROPAGATION_MANDATORY = 2;
    int PROPAGATION_REQUIRES_NEW = 3;
    int PROPAGATION_NOT_SUPPORTED = 4;
    int PROPAGATION_NEVER = 5;
    int PROPAGATION_NESTED = 6;
}
  • PROPAGATION_REQUIRED:若是當前沒有事務,就新建一個事務,若是已經存在一個事務中,加入到這個事務中。這是最多見的選擇。
  • PROPAGATION_SUPPORTS:支持當前事務,若是當前沒有事務,就以非事務方式執行。
  • PROPAGATION_MANDATORY:使用當前的事務,若是當前沒有事務,就拋出異常。
  • PROPAGATION_REQUIRES_NEW:新建事務,若是當前存在事務,把當前事務掛起。
  • PROPAGATION_NOT_SUPPORTED:以非事務方式執行操做,若是當前存在事務,就把當前事務掛起。
  • PROPAGATION_NEVER:以非事務方式執行,若是當前存在事務,則拋出異常。
  • PROPAGATION_NESTED:若是當前存在事務,則在嵌套事務內執行。若是當前沒有事務,則執行與PROPAGATION_REQUIRED相似的操做。

經常使用的主要有Required,RequiresNew,Nested三種。源碼

  • Required:簡單理解就是事務方法會判斷是否存在事務,有事務就用已有的,沒有就從新開啓一個。
  • RequiresNew:簡單理解就是開啓新事務,若當前已有事務,掛起當前事務。新開啓的事務和以前的事務無關,擁有本身的鎖和隔離級別,能夠獨立提交和回滾,內層事務執行期間,外層事務掛起,內層事務執行完成後,外層事務恢復執行。
  • Nested:簡單理解就是嵌套事務,若是外部事務回滾,則嵌套事務也會回滾!!!外部事務提交的時候,嵌套它纔會被提交。嵌套事務回滾不會影響外部事務。子事務是上層事務的嵌套事務,在子事務執行以前會創建savepoint,嵌套事務的回滾會回到這個savepoint,不會形成父事務的回滾。

若是想事務一塊兒執行能夠用Required知足大部分場景,若是不想讓執行的子事務的結果影響到父事務的提交能夠將子事務設置爲RequiresNew。it

嵌套是子事務套在父事務中執行,子事務是父事務的一部分,在進入子事務以前,父事務創建一個回滾點,叫save point,而後執行子事務,這個子事務的執行也算是父事務的一部分,而後子事務執行結束,父事務繼續執行。重點就在於那個save point。看幾個問題就明瞭了:

若是子事務回滾,會發生什麼? 父事務會回滾到進入子事務前創建的save point,而後嘗試其餘的事務或者其餘的業務邏輯,父事務以前的操做不會受到影響,更不會自動回滾。

若是父事務回滾,會發生什麼? 父事務回滾,子事務也會跟着回滾!爲何呢,由於父事務結束以前,子事務是不會提交的,咱們說子事務是父事務的一部分,正是這個道理。那麼:

事務的提交,是什麼狀況? 是父事務先提交,而後子事務提交,仍是子事務先提交,父事務再提交?答案是第二種狀況,仍是那句話,子事務是父事務的一部分,由父事務統一提交。

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. 潛套事務是外部事務的一部分, 只有外部事務結束後它纔會被提交。

因而可知, PROPAGATION_REQUIRES_NEW 和 PROPAGATION_NESTED 的最大區別在於, PROPAGATION_REQUIRES_NEW 徹底是一個新的事務, 而 PROPAGATION_NESTED 則是外部事務的子事務, 若是外部事務 commit, 嵌套事務也會被 commit, 這個規則一樣適用於 roll back.

PROPAGATION_REQUIRED應該是咱們首先的事務傳播行爲。它可以知足咱們大多數的事務需求。

事務狀態TransactionStatus

TransactionStatus接口的一個實現,這個接口的內容以下:

public interface TransactionStatus{
    boolean isNewTransaction(); // 是不是新的事物
    boolean hasSavepoint(); // 是否有恢復點
    void setRollbackOnly();  // 設置爲只回滾
    boolean isRollbackOnly(); // 是否爲只回滾
    boolean isCompleted; // 是否已完成
}

能夠發現這個接口描述的是一些處理事務提供簡單的控制事務執行和查詢事務狀態的方法,在回滾或提交的時候須要應用對應的事務狀態。

事務註解

@Transactional(propagation=Propagation.REQUIRES_NEW,
            isolation=Isolation.READ_COMMITTED,
            noRollbackFor={UserAccountException.class},
            readOnly=true, timeout=3)

1.添加事務註解 使用propagation 指定事務的傳播行爲,即當前的事務方法被另一個事務方法調用時如何使用事務。 默認取值爲REQUIRED,即便用調用方法的事務 REQUIRES_NEW:使用本身的事務,調用的事務方法的事務被掛起。

2.使用isolation 指定事務的隔離級別,最經常使用的取值爲READ_COMMITTED。

3.默認狀況下 Spring 的聲明式事務對全部的運行時異常進行回滾,也能夠經過對應的屬性進行設置。一般狀況下,默認值便可。

4.使用readOnly 指定事務是否爲只讀。 表示這個事務只讀取數據但不更新數據,這樣能夠幫助數據庫引擎優化事務。若真的是一個只讀取數據庫值得方法,應設置readOnly=true。

5.使用timeOut 指定強制回滾以前事務能夠佔用的時間。

最後

回到琪姐那個問題上,既然修改了傳播機制能夠正常提交了,那麼說明須要開啓兩個事務才能夠提交,並且捕獲的異常是Excepiton(RollbackFor)。還存在幾個問題待解決:

  • 如今兩個update方法的隔離級別是RC,會不會和隔離級別有關呢?好比數據死鎖?
  • 以前線程池執行和主線程執行在傳播機制,隔離級別有區別嗎?
相關文章
相關標籤/搜索