聊聊Spring註解@Transactional失效的那些事

来源:https://www.cnblogs.com/jingdongkeji/archive/2023/07/18/17562092.html
-Advertisement-
Play Games

## **一、前言** emm,又又又踩坑啦。這次的需求主要是對逾期計算的需求任務進行優化,現有的計算任務運行時間太長了。簡單描述下此次的問題:**在項目中進行多個資料庫執行操作時,我們期望的是將其整個封裝成一個事務,要麼全部成功,或者全部失敗,然而在自測異常場景時發現,裡面涉及的第一個數據狀態更新 ...


一、前言

emm,又又又踩坑啦。這次的需求主要是對逾期計算的需求任務進行優化,現有的計算任務運行時間太長了。簡單描述下此次的問題:在項目中進行多個資料庫執行操作時,我們期望的是將其整個封裝成一個事務,要麼全部成功,或者全部失敗,然而在自測異常場景時發現,裡面涉及的第一個數據狀態更新成功了,但是後面的數據在插入出現異常,後面查詢數據表發現,該數據的狀態已經被更新成功啦

emmm,查看代碼發現確實是使用了@Transactional註解沒問啊。於是通過查詢網上相關資料發現,在使用Spring中事務註解@Transactional時會存在幾種場景下該註解失效,即不能按照預期封裝成一個事務操作,於是對該註解進行學習並對相關失效場景進行分析,整理文章如下;

二、@Transactional註解失效場景實例驗證

1、@Transactional註解屬性

屬性 類型 描述
value String 可選的限定描述符,指定使用的事務管理器
propagation Enum:Propagation· 可選的事務傳播行為設置
isolation Enum:Isolation 可選的事務隔離級別設置
readOnly boolean 讀寫或只讀事務,預設讀寫
timeout int 事務超時時間設置
rollbackFor Class對象數組,必須繼承自Throwable 導致事務回滾的異常類數組
rollbackForClassName 類名數組,必須繼承自Throwable 導致事務回滾的異常類名字數組
noRollbackFor Class對象數組,必須繼承自Throwable 不會導致事務回滾的異常類數組
noRollbackForClassName 類名數組,必須繼承自Throwable 不會導致事務回滾的異常類名字數組

2、 propagation屬性

propagation代表事務的傳播行為,預設值為Propagation.REQUIRED

屬性 描述
Propagation.REQUIRED 若當前存在事務則加入該事務,若不存在則創建一個新事務(預設)
Propagation.SUPPORTS 若當前存在事務則加入該事務,若不存在則以非事務的方式繼續進行
Propagation.MANDATORY 若當前存在事務則加入該事務,若不存在則拋出異常
Propagation.REQUIRES_NEW 重新創建一個新的事務,若當前存在事務則暫定當前事務
Propagation.NOT_SUPPORTED 以非事務的方式運行,若當前存在事務則暫定當前事務
Propagation.NEVER 以非事務的方式運行,若當前存在事務則拋出異常
Propagation.NESTED 與Propagation.REQUIRED效果一樣

3、 @Transactional註解使用場景?

@Transactional註解可以作用在介面、類、類方法中。

  • 當作用於類時,表示所有該類的public方法都配置相同的事務屬性信息。

  • 當作用於方法時,當類配置了@Transactional註解,方法也配置了@Transactional,方法的事務會覆蓋類的事務配置信息。

  • 當作用於介面時,不推薦使用,因為在介面使用@Transactional並且配置了Spring AOP使用CGLib動態代理將會導致其失效。

4、 @Transactional註解失效場景?

  • @Transactional註解作用在非public修飾的方法上,會失效。

失效原因:在Spring AOP代理時,TransactionInterceptor(事務攔截器)在目標方法執行前後進行攔截,DynamicAdvisedInterceptor(CglibAopProxy的內部類)的Intercept方法或JDKDynamicAopProxyinvoke方法會間接調用AbstractFallbackTransationAttributeSourcecomputeTransactionAttribute方法,獲取@Transactional註解的事務配置信息。

1 protected TransactionAttribute computeTransactionAttribute(Method method,
2    Class<?> targetClass) {
3        // Don't allow no-public methods as required.
4        if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
5        return null;
6    }

此方法會檢查目標方法的修飾符是否為public,非public作用域則不會獲取@transactional的屬性配置信息。其中protected、private修飾的方法上使用 @Transactional註解,事務會失效但不會有任何報錯。

  • @Transactional註解屬性propagation設置錯誤導致註解失效

失效原因:配置錯誤, PROPAGATION_SUPPORTS、PROPAGATION_NOT_SUPPORTED、PROPAGATION_NEVER三種事務傳播方式不會發生回滾。

▪ 實例驗證:寫了一個demo進行測試。demo主要功能如下:執行兩次資料庫插入操作,併在擴展信息欄位中添加備註;

▪ 運行結果如下,構造的單號不存在訂單查詢為空觸發異常,觀察資料庫發現,第一次資料庫插入操作已經執行成功,故而驗證@Transactional註解失效

  • @Transactional註解屬性rollbackFor設置錯誤導致註解失效

rollbackFor可以指定能夠觸發事務回滾的異常類型。Spring預設拋出了unchecked異常(繼承自RuntimeException)或者Error才會回滾事務。若事務中拋出了其他類型的異常,但卻期望Spring能夠回滾事務,就需要指定rollbackFor屬性,否則就會失效。

  • 同一類中方法調用,導致@Transactional失效

比如類demo中有方法A和B,方法B中使用@Transactional註解,方法A沒有註解,但是demo類通過方法A調用方法B,像這種間接調用會導致方法B中的@Transactional事務註解失效。

失效原因:只有當事務方法被當前類以外的代碼調用時,才會有Spring生成的代理對象管理。(Spring AOP代理機製造成的)。

實例驗證:demo中構造場景為在同一個類中,在test方法中添加@Transactional註解,querRiskScore方法中不添加該註解,然後在querRiskScore方法中調用test方法;觀察下多個插入操作是否會因為異常而中斷回滾;

▪ 運行結果如下,還是通過構造的單號不存在訂單查詢為空觸發異常,觀察資料庫發現,第一次資料庫插入操作已經執行成功第二次數據插入操作失敗,並沒有因為異常而觸發事務操作,故而驗證@Transactional註解方法間的調用會失效

  • 多線程任務可能導致@Transaction案例失效

失效原因:線程不屬於Spring托管,故線程不能夠預設使用Spring的事務,也不能獲取Spring註入的bean,在被Spring聲明式事務管理的方法內開啟多線程,多線程內的方法不被事務控制。

  • 異常被方法內catch捕獲導致@Transactional失效

比如B方法內部拋了異常,而A方法此時try-catch了B方法的異常,則該事務不能正常回滾。

失效原因:因為B方法中拋出異常以後,標識當前事務需要rollback,但是A方法中由於你手動的捕獲這個異常併進行處理,A方法認為當前事務應該正常commit,此時就出現前後不一致,會拋出org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only異常。

實例驗證:這個場景的本質還是異常被捕獲導致無法正常的拋出,進而導致@Transactional註解無法正常工作,我簡化了下demo實例場景,構造場景如下:在querRiskScore方法中添加@Transactional註解,然後在querRiskScore方法中對異常進行捕獲;觀察下多個插入操作是否會因為異常而中斷回滾;

▪ 運行結果如下,還是通過構造的單號不存在訂單查詢為空觸發異常,但是我們在方法內部對該異常進行捕獲,並未向上層拋出,我們期望的場景是兩次數據插入執行失敗,但是觀察資料庫發現,第一次資料庫插入操作已經執行成功第二次數據插入執行成功,與我們的預期結果不符,故而驗證@Transactional註解在方法中異常被捕獲的場景中失效

究其原因:Spring的事務是在調用業務方法之前開始的,業務方法執行完畢之後才執行commit 或 rollback,事務是否執行取決於是否拋出runtime異常,如果拋出runtime exception併在你的業務方法中並沒有catch到的話,事務就會回滾。

三、“事務”知識回顧

1.什麼是事務?

事務(Transaction)是由一系列對系統中數據進行訪問與更新的操作組成的一個程式執行邏輯單元(Unit)。

通常我們所指的事務是指資料庫事務,使用資料庫事務有以下兩處優點:

  • 當多個應用程式併發訪問資料庫時,事務可以在這些應用程式之間提供一個隔離方法,以防止彼此的操作互相干擾

  • 事務為資料庫操作序列提供了一個從失敗恢復到正常狀態的方法,同時提供了資料庫即使在異常狀態下仍能保持數據一致性的方法。

2. 事務具有的特性?

原子性(Atomicity)、一致性(Consistency)、隔離性(Isolation)和持久性,簡稱事務的ACID特性。

  • 原子性

事務的原子性是指事務必須是一個原子的操作序列單元,即事務中包含的各項操作在一次執行過程中只會出現兩種狀態:全部成功執行、全部不執行。任何一項操作失敗都將導致整個事務失敗,同時其他已經被執行的操作都將被撤銷並回滾,只打所有的操作全部成功,整個事務才算是成功完成。

  • 一致性

事務的一致性是指事務的執行不能破壞資料庫數據的完整性和一致性,一個事務在執行之前和執行之後,資料庫都必須處於一致性狀態。也就是說,事務執行的結果必須是使資料庫從一個一致性狀態轉變到另一個一致性狀態,因此當資料庫只包含成功事務提交 的結果時,就能說資料庫處於一致性狀態。而如果資料庫系統在運行過程中發生故障, 有些事務尚未完成就被迫中斷,這些未完成的事務對資料庫所做的修改有一部分已寫入物理資料庫,這時資料庫就處於一種不正確的狀態,或者說是不一致的狀態。

  • 隔離性

事務的隔離性是指在併發環境中,併發的事務是相互隔離的,一個事務的執行不能被其他事務干擾。也就是說,不同的事務併發操縱相同的數據時,每個事務都有各自完整的數據空間,即一個事務內部的操作及使用的數據對其他併發事務是隔離的,併發執行的 各個事務之間不能互相干擾。

  • 持久性

事務一旦提交,其所做的修改就會永久保存到資料庫中,即使資料庫發生故障也不應該對其有任何影響。需要註意的是,事務的持久性不能做到100%的持久,只能從事務本身的角度來保證永久性,而一些外部原因導致資料庫發生故障,如硬碟損壞,那麼所有提交的數據可能都會丟失。

3. 什麼是Spring中的事務?

Spring中同樣提供了很好的事務管理機制,主要分為編程式事務聲明式事務

  • 編程式事務

是指在代碼中手動的管理事務的提交、回滾等操作,代碼侵入性比較強。編程式事務方式需要開發者在代碼中手動的管理事務的開啟、提交、回滾等操作。

public void test() {

      TransactionDefinition def = new DefaultTransactionDefinition();

      TransactionStatus status = transactionManager.getTransaction(def);

      try {

         // 事務操作

         // 事務提交

         transactionManager.commit(status);

      } catch (DataAccessException e) {

         // 事務提交

         transactionManager.rollback(status);

         throw e;

      }

}
  • 聲明式事務

聲明式事務是基於AOP面向切麵,它將具體業務和事務處理部分解耦,代碼侵入性很低,實際開發中比較常用。我們常用TX和AOP的xml配置文件方式和@Transactional註解方式。

▪聲明式事務的優點:

對代碼無侵入性,方法內只需要寫業務邏輯,節省很多代碼量。

▪聲明式事務的缺點:

1、聲明式事務粒度問題:聲明式事務的局限就是最小粒度要作用在方法上,且不適合耗時長高併發場景。

2、聲明式事務容易被開發者忽略,當事務嵌套的方法中存在RPC遠程調用、MQ發送、Redis更行、文件寫入等操作可能存在以下場景:

▪ 事務嵌套的方法中RPC調用成功了,但是本地事務回滾導致RPC調用無法回滾(暫不討論分散式事務)。

▪事務嵌套的方法中遠程調用會拉長整個事務周期,導致事務的資料庫連接一致被占用,類似操作過多會導致資料庫連接池耗盡。

3、聲明式事務使用錯誤會導致在某些場景下失效

四、總結

作者:京東科技 宋慧超

來源:京東雲開發者社區


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • # 預處理 ## 動態庫和靜態庫 > 庫: 將源文件生成的二進位文件 只需要鏈接即可生成可執行文件 ## 製作靜態庫 ``` linux gcc -c fun.c -o fun.o ar rc libtestlib.a fun.o ``` ### 使用靜態庫 #### 庫和工程在同一目錄下 ``` ...
  • ## 概念 定義:給定數集 $S$,以異或運算張成的數集與 $S$ 相同的極大線性無關集,稱為原數集的一個線性基。 簡單地說,線性基是一個數的集合。每個序列都擁有至少一個線性基。取線性基中若幹個數異或起來可以得到原序列中的任何一個數。 ## 性質 - 性質一 > - 取線性基中若幹個數異或起來可以得 ...
  • # 面向對象編程 根據類來創建對象稱為實例化。這裡只過一下大概的面向對象的內容,不做細講。可以直接查閱資料。https://www.runoob.com/python3/python3-class.html ## 創建和使用類及實例 給出一個類的使用例子: ```python class Dog: ...
  • # if 語句 給出一個簡單的示例 ```python cars = ["audi", "bmw", "subaru", "toyota"] for car in cars: if car == "bmw": print(car.upper()) else: print(car.title()) ` ...
  • 知道要轉型,要建設數據中台,卻不知咋做,咋辦? 現在有很多講“如何建設數據中台”文章,觀點各不相同: - 數據中台是數據建設方法論,按照數據中台設計方法和規範實施就可建成數據中台 - 數據中台背後是數據部門組織架構變更,把原先分散的組織架構形成一個統一中台部門,就建成數據中台 - 一些大數據公司說, ...
  • 本文介紹了網路IO模型,引入了epoll作為Linux系統中高性能網路編程的核心工具。通過分析epoll的特點與優勢,並給出使用epoll的註意事項和實踐技巧,該文章為讀者提供了寶貴的指導。 ...
  • 去年看到位元組跳動給golang提了issue建議把map的底層實現改成SwissTable的時候,我就有想寫這篇博客了,不過因為種種原因一直拖著。 直到最近遇golang官方開始討論為了是否要接受SwissTable作為map的預設實現,以及實際遇到了一個hashtable有關的問題,促使我重新思考 ...
  • **在這篇文章中,我們會深入探討Python單元測試的各個方面,包括它的基本概念、基礎知識、實踐方法、高級話題,如何在實際項目中進行單元測試,單元測試的最佳實踐,以及一些有用的工具和資源** ## 一、單元測試重要性 測試是軟體開發中不可或缺的一部分,它能夠幫助我們保證代碼的質量,減少bug,提高系 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...