Spring Boot 的事務注解 @Transactional 失效的幾種情況

開發中我們經常會用到 Spring Boot 的事務注解,為含有多種操作的方法添加事務,做到如果某一個環節出錯,全部回滾的效果。但是在開發中可能會因為不了解事務機制,而導致我們的方法使用了 @Transactional 注解但是沒有生效的情況,下面就把這幾種不能生效的情況整理一下。

文章目錄

  • 一、非public方法(動態代理限制)
  • 二、自調用問題(類內部方法調用,不走代理)
  • 三、異常類型不匹配(默認只回滾RuntimeException)
  • 四、多線程切換(事務連接綁定ThreadLocal)
  • 五、錯誤傳播行為(如:PROPAGATION_NOT_SUPPORTED掛起事務)
  • 六、總結

一、非public方法(動態代理限制)

Spring 的事務管理本質上是通過 AOP 動態代理 實現的(JDK 動態代理或 CGLIB 代理)。

代理對象在調用目標方法時,會添加事務管理的邏輯(開啟事務、提交/回滾事務)。

然而,動態代理只能代理 public 方法。

如果你將 @Transactional 注解放在 protectedprivate 或默認(包級私有)方法上,Spring 在創建代理時無法為這些方法添加事務增強邏輯。

當你通過代理對象調用這些非 public 方法時,事務相關的代碼(如 beginTransaction(), commit(), rollback())不會被織入,因此事務管理完全失效。

所以,要確保所有需要事務管理的方法都是 public 的。這是 Spring AOP 代理機制的一個硬性限制。

二、自調用問題(類內部方法調用,不走代理)

這是 AOP 代理機制帶來的另一個典型問題。假設一個 Service 類中有兩個方法:

  • methodA():沒有 @Transactional 注解。
  • methodB():有 @Transactional 注解。

如果你在 methodA() 內部直接調用 this.methodB(),那么你調用的是 Service 類本身的 methodA()this 指向目標對象本身)。methodA() 內部調用 this.methodB(),是目標對象內部的方法調用
這個調用完全不經過為該 Service 類生成的代理對象。

因為調用 methodB() 沒有經過代理對象,所以代理對象上附加的事務攔截邏輯根本不會被執行。methodB() 雖然標注了 @Transactional,但在此次調用中完全失效。


解決方案有以下幾種:推薦重構代碼。

方案一:注入自身代理對象

開啟 exposeProxy:在配置類(如 @SpringBootApplication 主類)上添加 @EnableAspectJAutoProxy(exposeProxy = true)

在需要自調用事務方法的地方獲取代理對象:

((YourServiceClass) AopContext.currentProxy()).methodB();

AopContext.currentProxy() 獲取到當前方法執行上下文中的代理對象(即被 Spring AOP 增強過的對象),通過這個代理對象調用 methodB(),就會走代理邏輯,事務攔截器生效。

這種方式不常用,會有缺點,引入了 Spring AOP 特定 API (AopContext),增加了代碼耦合度。

方案二:重構代碼(推薦)

將需要事務管理的業務邏輯 methodB() 抽取到另一個獨立的 Bean(如另一個 Service)中。然后在原來的 methodA() 中注入并使用這個新的 Bean 來調用 methodB()。這樣調用自然通過代理對象進行。

這是更符合設計原則(單一職責、依賴注入)的做法,避免了自調用問題,也降低了耦合。

方案三:使用 ApplicationContext 獲取 Bean

在類中注入 ApplicationContext,然后通過 ctx.getBean(YourServiceClass.class).methodB() 來調用。這樣獲取到的是代理 Bean,調用會走代理。

代碼略顯繁瑣,并且也需要依賴 Spring 容器。

三、異常類型不匹配(默認只回滾RuntimeException)

@Transactional 注解的 rollbackFor 屬性默認值是 RuntimeExceptionError

  • 當方法拋出 RuntimeException 或其子類(如 NullPointerException, IllegalArgumentException)時,Spring 會回滾事務。
  • 當方法拋出檢查型異常(如 IOException, SQLException)時,Spring 默認會提交事務!

如果你在一個事務方法中拋出了自定義的業務異常(繼承自 Exception 而非 RuntimeException),或者拋出了其他檢查型異常,并且沒有顯式配置 rollbackFor,那么即使業務邏輯出錯拋出了異常,Spring 也會正常提交事務,導致數據不一致。

這時,我們要顯式指定 rollbackFor:在 @Transactional 注解中明確聲明哪些異常需要觸發回滾。

// 回滾所有 Exception 和自定義異常
@Transactional(rollbackFor = {Exception.class, YourCustomBusinessException.class}) 
public void transactionalMethod() throws Exception { ... }

或者修改默認行為(謹慎):雖然不推薦,但可以通過修改 Spring 的全局事務管理器配置來改變默認的回滾異常類型(例如改為回滾所有 Throwable)。

但這樣做風險較大,可能回滾不應該回滾的異常(如 OutOfMemoryError)。

最佳實踐還是根據具體業務在注解上顯式配置 rollbackFornoRollbackFor

四、多線程切換(事務連接綁定ThreadLocal)

Spring 的事務管理核心是將數據庫連接(Connection)綁定到當前執行線程(Thread)的 ThreadLocal 變量上。

一個事務從開始(beginTransaction)到提交/回滾(commit/rollback)期間,所有數據庫操作都使用這個綁定在當前線程 ThreadLocal 上的同一個 Connection,以此保證 ACID 特性。

如果你在一個事務方法內部啟動了一個新線程(new Thread() 或者使用線程池(如 @Async)執行數據庫操作,會出現以下情況:

  • 新線程擁有自己獨立的 ThreadLocal 存儲。
  • 新線程無法訪問到原始事務線程綁定的 Connection 對象。
  • 新線程中的數據庫操作會從連接池獲取一個新的、獨立的 Connection
  • 這個新 Connection 不參與原始事務,其操作會在自身 autoCommit 模式下立即執行(通常是自動提交),與原始事務完全隔離。

新線程中的數據庫操作成功與否不影響原始事務的提交或回滾,反之亦然。破壞了事務的原子性(Atomicity)。原始事務回滾不會回滾新線程中的操作;新線程操作失敗也不會導致原始事務回滾。

解決方案:處理多線程下的數據一致性非常復雜,沒有銀彈:

  • **避免在事務方法內開啟異步線程執行 DB 操作:**這是最根本的預防措施。將需要在同一事務中完成的操作放在同一個線程內執行。
  • 編程式事務管理: 在新線程內部,使用 TransactionTemplate 手動管理事務邊界。但這只是讓新線程內部操作具有事務性,無法與原始線程的事務合并成一個原子事務。
  • **分布式事務:**如果業務強要求跨線程的 ACID,可能需要引入分布式事務管理器(如 Seata, Atomikos)來處理這種跨 資源(不同線程可視為不同資源管理者)的場景,但代價高昂且復雜。
  • 設計補償機制: 在業務層設計最終一致性方案(如 Saga 模式),通過記錄操作日志、發送消息、定時任務補償等方式,在異步操作失敗后嘗試回滾或修正原始事務已提交的操作。這是更常見的處理異步事務一致性的實踐。

五、錯誤傳播行為(如:PROPAGATION_NOT_SUPPORTED掛起事務)

@Transactionalpropagation 屬性定義了當前方法的事務如何與已存在的事務進行交互。使用不當會導致事務行為不符合預期。

PROPAGATION_NOT_SUPPORTED 不支持事務。如果當前存在事務,則掛起(Suspend) 這個事務;然后以非事務方式執行當前方法。方法執行完畢后,之前掛起的事務恢復(Resume)。

假設方法 outer() 開啟了一個事務(Propagation.REQUIRED),在其內部調用 inner() 方法,而 inner() 被標注為 @Transactional(propagation = Propagation.NOT_SUPPORTED),當執行到 inner() 時:

  1. 系統檢測到當前存在 outer() 開啟的事務。
  2. 根據 NOT_SUPPORTED 語義,掛起 outer() 的事務
  3. inner() 方法在無事務狀態下執行(相當于 autoCommit=true)。
  4. inner() 方法執行完畢(無論成功失敗,其操作已立即提交)。
  5. 恢復 outer() 的事務,繼續執行 outer() 剩余代碼。

結果是 inner() 方法中的數據庫操作不受 outer() 事務控制。即使 outer() 最終因異常回滾,inner() 中已提交的操作不會被回滾!這通常不是開發者想要的效果,極易造成數據不一致。

其他易錯傳播行為:

  • PROPAGATION_NEVER 要求不能存在事務。如果調用者在一個事務中調用了標記為 NEVER 的方法,會直接拋出 IllegalTransactionStateException 異常。
  • PROPAGATION_SUPPORTS 如果當前存在事務,就加入該事務;如果沒有,就以非事務方式執行。關鍵點在于非事務方式。如果方法中有多個操作且需要原子性,而外部又恰好沒有事務,這些操作就會各自獨立提交。
  • PROPAGATION_REQUIRES_NEW 總是開啟一個全新的、獨立的事務。會掛起外部事務(如果存在)。新事務的提交/回滾與外部事務互不影響。注意: 這雖然創建了新事務,但不同于自調用失效,它是有效的(通過代理調用)。它的陷阱在于開發者可能誤以為新事務是外部事務的一部分,其實它們是獨立的。

解決方案:

  • 深入理解傳播行為: 務必清楚每種傳播行為(REQUIRED, REQUIRES_NEW, SUPPORTS, MANDATORY, NOT_SUPPORTED, NEVER, NESTED)的精確語義。
  • 謹慎選擇傳播行為: 默認使用 Propagation.REQUIRED 通常能滿足大多數場景(加入現有事務,沒有則新建)。只有在有明確且充分理由時才使用其他傳播行為。
  • 代碼審查與測試: 對使用了非默認傳播行為的代碼進行重點審查,并通過單元測試、集成測試模擬各種調用鏈路,驗證事務邊界和回滾行為是否符合預期。特別注意跨方法、跨服務調用時的事務傳播。

六、總結

Spring Boot 事務失效的核心原因通常圍繞:

  • AOP 代理機制的限制(非 public、自調用)
  • 異常處理機制(默認回滾異常類型)
  • 資源綁定機制(ThreadLocal 導致多線程失效)
  • 配置錯誤(傳播行為誤用)

解決這些問題需要深入理解 Spring 事務管理的底層原理(代理、ThreadLocal、異常回滾規則、傳播語義),并在編碼和配置時保持謹慎,遵循最佳實踐(如方法 public、避免自調用、顯式指定 rollbackFor、理解傳播行為、避免事務內跨線程操作 DB)。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/91847.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/91847.shtml
英文地址,請注明出處:http://en.pswp.cn/web/91847.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

#C語言——刷題攻略:牛客編程入門訓練(四):運算

🌟菜鳥主頁:晨非辰的主頁 👀學習專欄:《C語言刷題合集》 💪學習階段:C語言方向初學者 ?名言欣賞:"代碼行數決定你的下限,算法思維決定你的上限。" 目錄 1. BC25 牛牛買電…

阻抗分析中的軟件解調計算

接上篇 重溫無功功率測量-CSDN博客 已知被測阻抗兩端電壓與流過 通過兩個ADC同步采集到。 激勵頻率10k, 采樣率1M, 每周期100個點 關鍵是:采樣率除以激勵頻率, 得是4的倍數... 所以ADC不能自由運行, 得用一個timer來觸發. 因為要進行同相分量正交分量計算。 1:直…

ubuntu 鏡像克隆

一、克隆 1、準備 一個u盤(制作啟動盤) 一個移動固態硬盤(大于要克隆系統盤的1.2倍) 2、使用 rufus生成系統啟動盤 (1)下載ubuntu iso 桌面版 https://cn.ubuntu.com/download (2&#x…

Axure下拉菜單:從基礎交互到高保真元件庫應用

在Web端產品設計中,下拉菜單(Dropdown Menu) 是用戶與系統交互的核心組件之一,它通過隱藏次要選項、節省頁面空間的方式,提升信息密度與操作效率。無論是基礎下拉菜單、圖標式下拉菜單,還是復雜的多級下拉菜…

復現YOLOV5+訓練指定數據集

一、復現YOLOV5代碼 1.github下載:https://github.com/MIPIT-Team/SSA-YOLO 2.配置環境:創建虛擬環境yolo5 conda create -n yolo5 python3.9 #對應文件夾下pip install -r requirements.txt報錯:ERROR: pips dependency resolver does no…

Agents-SDK智能體開發[4]之集成MCP入門

文章目錄說明一 Agents SDK接入MCP1.1 MCP技術回顧1.2 MCP基礎實踐流程1.2.1 天氣查詢服務器Server創建流程1.2.2 服務器依賴安裝和代碼編寫1.2.3 環境配置文件1.2.4 客戶端代碼編寫1.3 測試運行二 MCPAgents SDK基礎調用2.1 weather_server.py2.2 client_agent.py2.3 運行測試…

Camera相機人臉識別系列專題分析之十九:MTK ISP6S平臺FDNode傳遞三方FFD到APP流程解析

【關注我,后續持續新增專題博文,謝謝!!!】 上一篇我們講了: 這一篇我們開始講: Camera相機人臉識別系列專題分析之十九:MTK平臺FDNode傳遞三方FFD到APP流程解析 目錄 一、背景 二、:OcamMeta傳遞FFD到APP 2.1:OcamMeta 2.2 :OcamMeta::process更新FFD 2.…

【實時Linux實戰系列】構建實時監測與報警系統

在實時系統中,監測與報警系統是確保系統正常運行和及時響應異常情況的關鍵組件。實時監測與報警系統能夠實時收集系統數據,分析關鍵事件,并在檢測到異常時發出警報。這種系統廣泛應用于工業自動化、醫療設備監控、網絡安全等領域。掌握實時監…

PHP入門及數據類型

PHP數據類型 PHP標記 //HTML風格 <?phpecho "hello world"; ?> //簡短風格 <?echo "hello world"; ?>數據類型 PHP 最初源于 Perl 語言&#xff0c;與 Perl 類似&#xff0c;PHP 對數據類型采取較為寬松的態度。PHP 規定&#xff0c;變量數…

沸點 | 嬴圖參加世界人工智能大會

2025 WAIC于 7 月 26 日至 28 日在上海舉行。大會展覽面積突破 7 萬平方米&#xff0c;800 余家企業參展。嬴圖作為圖數據庫領域的領先企業&#xff0c;攜前沿技術與創新應用精彩亮相。?大會期間&#xff0c;嬴圖創始人兼CEO孫宇熙與來自全球的頂尖學者、企業代表共同探討人工…

2. 字符設備驅動

一、設備號 1.1. 什么是設備號 設備號是用來標記一類設備以及區分這類設備中具體個體的一組號碼。 設備號由主設備號和次設備號組成。主設備號的作用為標記一類設備、用于標識設備驅動程序,而次設備號的作用是為了區分這類設備中的具體個體設備及用于標識同一驅動程序下的具…

uboot armv8 啟動流程之 linker script

section 詳細說明.text按如下順序&#xff0c;中斷向量表vectors, 啟動入口代碼start.o,普通text, glue &#xff08;arm thumb2 相互調用時自動生成的代碼&#xff09;*(.vectors)CPUDIR/start.o (.text*)*(.text*)*(.glue*)__image_copy_start 標記為text 段入口&#xff0c;…

xxljob總結

XXL-Job 支持多種任務類型&#xff0c;以下是常見任務類型的示例 Demo&#xff0c;包含核心配置和代碼片段&#xff0c;幫助快速理解用法&#xff1a;一、Bean模式任務&#xff08;最常用&#xff09;通過注解 XxlJob 定義任務方法&#xff0c;直接在 Spring 容器中管理&…

Python包安全工程實踐:構建安全可靠的Python生態系統

在現代計算環境中&#xff0c;性能往往是Python包成功的關鍵因素。本文將深入探討Python包的性能優化技術&#xff0c;包括并發編程模型、性能分析工具、內存優化策略以及原生代碼集成等高級主題&#xff0c;幫助你構建高性能的Python組件。1. 性能分析基礎1.1 性能分析工具矩陣…

kubernetes基礎知識

個人博客站—運維鹿: http://www.kervin24.top CSDN博客—做個超努力的小奚&#xff1a; https://blog.csdn.net/qq_52914969?typeblog一、kubernetes介紹Kubernetes本質是一組服務器集群&#xff0c;它可以在集群的每個節點上運行特定的程序&#xff0c;來對節點中的容器進行…

winntsetup安裝驅動和光驅安裝F6功能一樣----NT5.2.3790源代碼分析

D:\drv>dir驅動器 D 中的卷是 新加卷卷的序列號是 443D-D64BD:\drv 的目錄2025-08-03 23:57 <DIR> . 2025-08-03 23:57 <DIR> .. 2008-05-27 10:01 119,068 yk51x86.cat 2008-05-20 10:01 969,380 yk51x86.inf…

Web 開發 11

今天完成了workshop2&#xff0c;進度有點慢&#xff0c;但是記錄一下極為愚蠢的一輪輪問答和思考~&#xff01;&#xff08;還是有點成就感的&#xff09;ps&#xff1a;【】內為我的提問1 導入語句&#xff08;ES6 模塊導入語法&#xff09;【import CatHappiness from "…

寫作路上的迷茫與突破

曾經&#xff0c;我也是那個在寫作面前躊躇不前的人。每次提筆&#xff0c;滿心都是“我寫不好”“我沒什么可寫的”“我達不到別人的高度”……這些念頭像藤蔓一樣&#xff0c;緊緊纏繞著我&#xff0c;讓我寸步難行。我看著群里的小伙伴們一個個妙筆生花&#xff0c;自己卻只…

23 Active Directory攻擊與防護策略解析

引言 Active Directory&#xff08;AD&#xff09;是企業IT環境中用戶認證、訪問控制和身份管理的核心。因其掌握整個網絡的"鑰匙"&#xff0c;AD常成為攻擊者的首要目標。 從憑證轉儲到隱蔽偵察&#xff0c;攻擊者通過多種手段控制AD。無論您是網絡安全分析師、紅…

【內容規范】關于標題中【】標記的使用說明

【內容規范】關于標題中【】標記的使用說明 在信息爆炸的時代&#xff0c;如何讓內容更易識別、更具條理性&#xff0c;成為內容創作者和平臺運營者共同關注的問題。標題中【】標記的使用&#xff0c;正是在這種需求下形成的一種實用規范。 這種規范的核心作用在于建立統一的內…