1. 程式人生 > 其它 >Spring @Transactional 事務機制

Spring @Transactional 事務機制

技術標籤:javajavaTransactional

原文連結

https://www.cnblogs.com/leeego-123/p/11498327.html

摘選一下精華

在service類前加上@Transactional,宣告這個service所有方法需要事務管理。每一個業務方法開始時都會開啟一個事務。error是一定會回滾的

Spring預設情況下會對執行期例外(RunTimeException)進行事務回滾。這個例外是unchecked

如果遇到checked意外就不回滾。

如何改變預設規則:

1 讓checked例外也回滾:在整個方法前加上 @Transactional(rollbackFor=Exception.class)

2 讓unchecked例外不回滾: @Transactional(notRollbackFor=RunTimeException.class)

3 不需要事務管理的(只查詢的)方法:@Transactional(propagation=Propagation.NOT_SUPPORTED)

4 如果不新增rollbackFor等屬性,Spring碰到Unchecked Exceptions都會回滾,不僅是RuntimeException,也包括Error。

注意:

如果異常被try{}catch{}了,事務就不回滾了,如果想讓事務回滾必須再往外拋try{}catch{throw Exception}。

1.檢查型異常(Checked Exception)

  個人理解:所謂檢查(Checked)是指編譯器要檢查這類異常,檢查的目的一方面是因為該類異常的發生難以避免,另一方面就是讓開發者去解決掉這類異常,所以稱為必須處理(try ...catch)的異常。如果不處理這類異常,整合開發環境中的編譯器一般會給出錯誤提示。

  例如:一個讀取檔案的方法程式碼邏輯沒有錯誤,但程式執行時可能會因為檔案找不到而丟擲FileNotFoundException,如果不處理這些異常,程式將來肯定會出錯。所以編譯器會提示你要去捕獲並處理這種可能發生的異常,不處理就不能通過編譯。

2.非檢查型異常(Unchecked Exception)

  個人理解:所謂非檢查(Unchecked)是指編譯器不會檢查這類異常,不檢查的則開發者在程式碼的編輯編譯階段就不是必須處理,這類異常一般可以避免,因此無需處理(try ...catch)。如果不處理這類異常,整合開發環境中的編譯器也不會給出錯誤提示。

  例如:你的程式邏輯本身有問題,比如陣列越界、訪問null物件,這種錯誤你自己是可以避免的。編譯器不會強制你檢查這種異常。

坑點: 在業務層捕捉異常後,發現事務不生效。

這是許多新手都會犯的一個錯誤,在業務層手工捕捉並處理了異常,你都把異常“吃”掉了,Spring自然不知道這裡有錯,更不會主動去回滾資料。例如:下面這段程式碼直接導致增加餘額的事務回滾沒有生效。

複製程式碼

 @Transactional(rollbackFor = Exception.class)
    public void addMoney() throws Exception {
        //先增加餘額
        accountMapper.addMoney();
        //謹慎:儘量不要在業務層捕捉異常並處理
        try {
            throw new SQLException("發生異常了..");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

複製程式碼如果捕捉的異常是一個外部類發出的異常,就算捕捉了也會全部回滾,spring boot 預設的事物即發生異常無論是否捕獲均會回滾。

若非實際業務要求,則在業務層統一丟擲異常,然後在控制層統一處理。

上面連結寫的很好啊,推薦下,然後我這邊自己總結下一般如何標註

我下面的情況都是預設以這個方式寫的進行下面的總結,rollbackFor = Exception.class我想把所有的異常都回滾。如果不是propagation = Propagation.REQUIRED或者其他REQUIRES-NEW等的話,不在該總結範圍內

@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)

一 呼叫本類的方法

開啟事務和事務回滾,實際這個過程是aop代理幫忙完成的,當呼叫一個方法時,它會先檢查時候有事務,有則開啟事務,
呼叫本類的方法是,它並沒有將其視為proxy呼叫,而是方法的直接呼叫,所以也就沒有檢查該方法是否含有事務這個過程,
那麼本地方法呼叫的事務也就無效了。

因此總結:都有事務的方法,在不同類中是有起到作用的。

但是在同一個類中事務的情況就不一樣了

1 a與b都有事務,若A.a(呼叫方)呼叫A.b(被呼叫方),則b的事務是不會開啟的,被忽略掉。

2.a無事務,b有事務,如果A.a呼叫A.b 此時b事務被忽略,代表事務都沒有開。 如果此時A .a捕獲了b的話,b所在異常語句位置沒有回滾外,其他b位置還是commit了,包括呼叫方a也是commit,就同平常無加事務的情況

3 a有事務,B無,b異常,則都回滾

二 呼叫外部類的方法(A類 B類)

1. A有事務,B也有事務

A呼叫B方法,B異常,A不捕獲,那麼AB都會回滾

A呼叫B方法,B異常,A捕獲了呼叫B的異常,那麼AB也會回滾

丟擲如下異常Transaction rolled back because it has been marked as rollback-only;資料庫中資料也沒有被修改。總結一下,spring boot 預設的事物即發生異常無論是否捕獲均會回滾。

2. A有事務,B無事務,C有事務(A類呼叫外部B類,B呼叫外部C類)

C發生異常,B進行捕獲了。但最終A B C 全部回滾了

丟擲如下異常Transaction rolled back because it has been marked as rollback-only;資料庫中資料也沒有被修改。總結一下,spring boot 預設的事物即發生異常無論是否捕獲均會回滾。

3. A.a有事務,B.a無事務,B.b有事務(A類呼叫B.a,B.a呼叫B.b)

B.b發生異常,在B.a中進行捕獲,最終都commit了,A.a無感知,正常了走完了整個流程。只有B.b異常位置沒有往下執行,在B.b方法內的前面的語句還是commit了

B.a無事務,呼叫同類的有異常的事務,會被自動忽略,因此直接認為B.a跟B.b無事務,在含有事務的A那裡是沒有感知到有異常發生,因此commit了

綜合重點

以上都是以 @Transactional(rollbackFor = Exception.class)進行測試的。

1 同個類的被呼叫方事務預設被忽略

2 外部間的呼叫,呼叫方有事務,看被呼叫方是否是有意義的事務(同類被忽略,可看上面規則,a1有事務,a2有事務,則預設a2已有事務,不管a2有沒有寫事務,如果a1無事務,a2有,預設a1 a2都不具備事務意義),

1)任何一個當發生異常未捕獲時,全回滾。

2)當發生異常且捕獲時,類之間的呼叫方 若是沒被忽略,其具備事務意義,則全部回滾,若所在被呼叫類不具備事務意義,則不回滾

3)只要主呼叫方感知到了有異常,且沒有捕捉,一律回滾