SpringBoot @Transactional註解失效排查

問題描述:最近發現一個加了@Transactional註解的事務沒有生效

問題分析:

一、註解特性

1、service類標籤(一般不建議在接口上)上添加@Transactional,可以將整個類納入spring事務管理,在每個業務方法執行時都會開啓一個事務,不過這些事務採用相同的管理方式。

2、@Transactional 註解只能應用到 public 可見度的方法上。 如果應用在protected、private或者 package可見度的方法上,也不會報錯,不過事務設置不會起作用。

3、默認情況下,Spring會對unchecked異常進行事務回滾;如果是checked異常則不回滾。
辣麼什麼是checked異常,什麼是unchecked異常

java裏面將派生於Error或者RuntimeException(比如空指針,1/0)的異常稱爲unchecked異常,其他繼承自java.lang.Exception得異常統稱爲Checked Exception,如IOException、TimeoutException等

辣麼再通俗一點:你寫代碼出現的空指針等異常,會被回滾,文件讀寫,網絡出問題,spring就沒法回滾了。然後我教大家怎麼記這個,因爲很多同學容易弄混,你寫代碼的時候有些IOException我們的編譯器是能夠檢測到的,說以叫checked異常,你寫代碼的時候空指針等死檢測不到的,所以叫unchecked異常。這樣是不是好記一些啦

4、只讀事務:

@Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true)

只讀標誌只在事務啓動時應用,否則即使配置也會被忽略。

啓動事務會增加線程開銷,數據庫因共享讀取而鎖定(具體跟數據庫類型和事務隔離級別有關)。通常情況下,僅是讀取數據時,不必設置只讀事務而增加額外的系統開銷。

二、事務傳播行爲

傳播行爲 意義
PROPAGATION_MANDATORY 表示該方法必須運行在一個事務中。如果當前沒有事務正在發生,將拋出一個異常
PROPAGATION_NESTED 表示如果當前正有一個事務在進行中,則該方法應當運行在一個嵌套式事務中。被嵌套的事務可以獨立於封裝事務進行提交或回滾。如果封裝事務不存在,行爲就像PROPAGATION_REQUIRES一樣。
PROPAGATION_NEVER 表示當前的方法不應該在一個事務中運行。如果一個事務正在進行,則會拋出一個異常。
PROPAGATION_NOT_SUPPORTED 表示該方法不應該在一個事務中運行。如果一個現有事務正在進行中,它將在該方法的運行期間被掛起。
PROPAGATION_SUPPORTS 表示當前方法不需要事務性上下文,但是如果有一個事務已經在運行的話,它也可以在這個事務裏運行。
PROPAGATION_REQUIRES_NEW 表示當前方法必須在它自己的事務裏運行。一個新的事務將被啓動,而且如果有一個現有事務在運行的話,則將在這個方法運行期間被掛起。
PROPAGATION_REQUIRES 表示當前方法必須在一個事務中運行。如果一個現有事務正在進行中,該方法將在那個事務中運行,否則就要開始一個新事務。

三、事務隔離級別

在一個典型的應用程序中,多個事務同時運行,經常會爲了完成他們的工作而操作同一個數據。併發雖然是必需的,但是會導致一下問題:

  • 髒讀(Dirty read)-- 髒讀發生在一個事務讀取了被另一個事務改寫但尚未提交的數據時。如果這些改變在稍後被回滾了,那麼第一個事務讀取的數據就會是無效的。
  • 不可重複讀(Nonrepeatable read)-- 不可重複讀發生在一個事務執行相同的查詢兩次或兩次以上,但每次查詢結果都不相同時。這通常是由於另一個併發事務在兩次查詢之間更新了數據。
  • 幻影讀(Phantom reads)-- 幻影讀和不可重複讀相似。當一個事務(T1)讀取幾行記錄後,另一個併發事務(T2)插入了一些記錄時,幻影讀就發生了。在後來的查詢中,第一個事務(T1)就會發現一些原來沒有的額外記錄。
隔離級別 含義
ISOLATION_DEFAULT 使用後端數據庫默認的隔離級別。
ISOLATION_READ_UNCOMMITTED 允許讀取尚未提交的更改。可能導致髒讀、幻影讀或不可重複讀。
ISOLATION_READ_COMMITTED 允許從已經提交的併發事務讀取。可防止髒讀,但幻影讀和不可重複讀仍可能會發生。
ISOLATION_REPEATABLE_READ 對相同字段的多次讀取的結果是一致的,除非數據被當前事務本身改變。可防止髒讀和不可重複讀,但幻影讀仍可能發生。
ISOLATION_SERIALIZABLE 完全服從ACID的隔離級別,確保不發生髒讀、不可重複讀和幻影讀。這在所有隔離級別中也是最慢的,因爲它通常是通過完全鎖定當前事務所涉及的數據表來完成的。

 四、檢查範圍

1、檢查你方法是不是public的

2、你的異常類型是不是unchecked異常
如果我想check異常也想回滾怎麼辦,註解上面寫明異常類型即可

1

@Transactional(rollbackFor=Exception.class)

類似的還有norollbackFor,自定義不回滾的異常

3、數據庫引擎要支持事務,如果是MySQL,注意表要使用支持事務的引擎,比如innodb,如果是myisam,事務是不起作用的

4、是否開啓了對註解的解析

1

<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>

5、spring是否掃描到你這個包,如下是掃描到org.test下面的包

1

<context:component-scan base-package="org.test" ></context:component-scan>

6、檢查是不是同一個類中的方法調用(如a方法調用同一個類中的b方法)

7、異常是不是被你catch住了

結果處理:經過以上的分析發現原來是屬於情況6,在同一個方法中A調用B,B方法的沒有生效。

其他情況:另外情況1,方法沒有設置成public也會日常中常見的錯誤。

參考:https://www.jb51.net/article/177053.htm

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