Spring 學習筆記之 @Transactional 異常不回滾匯總

使用springboot時,只要引入spring-jdbc/jpa相關的依賴后,在想要啟用事務的方法上加上@Transactional注解就能開啟事務,碰到異常就能自動回滾。大大的提高了編碼的便捷性性,同時也不侵入代碼,保持了代碼的簡潔性。

默認情況下,Spring時使用的Spring AOP (mode=Mode.Proxy, proxyTargetClass=false)方式啟動數據庫事務攔截。只有了解清楚了具體背景,才能清除知道事務為什么在碰到異常時沒有能夠正確回滾。下面是一些常用場景分析:

場景1、未正確配置TransactionManager

使用springboot開發時,引入以下依賴后通常會自動啟用TransactionManager。

  • spring-boot-starter-jdbc?是 Spring Boot 提供的用于簡化 JDBC(Java Database Connectivity)開發的啟動器,引入該依賴后,Spring Boot 會自動配置?DataSourceTransactionManager

  • spring-boot-starter-data-jpa?是 Spring Boot 提供的用于簡化 JPA(Java Persistence API)開發的啟動器,它集成了 Hibernate 等 JPA 實現框架,方便開發者進行數據庫操作。引入該依賴后,Spring Boot 會自動配置?JpaTransactionManager

通過下面代碼中的printTransactionManager(TransactionManager transactionManager) 方法可以檢查是否配置正常。

@SpringBootApplication
public class MybatisApplication {public static void main(String[] args) {org.springframework.boot.SpringApplication.run(MybatisApplication.class, args);}@BeanObject printTransactionManager(TransactionManager transactionManager) {System.out.println("transactionManager: " + transactionManager);return null;}
}

transactionManager: org.springframework.jdbc.support.JdbcTransactionManager@590765c4?

通過打印語句,可以看到spring中的TransactionManager是否正確配置。

場景2、@Transaction注解不在public方法上

默認情況下,事務是在proxy模式下(即Spring AOP負責攔截事務),proxyTargetClass=false ,有接口的時候使用JDK動態代理實現。沒有接口時使用CGLIB進行代理。

JDK代理接口時,都是public方法。

CGLIB代理時,在public方法上能生效。在Spring 6.0 以后,除public方法外,可以代理protected, package-visable修飾的方法。

當@Transactional注解位于private/final修飾的方法上時,事務碰到異常不能正常回滾。

詳情參考文檔:

Method visibility and?@Transactional?in proxy mode

The?@Transactional?annotation is typically used on methods with?public?visibility. As of 6.0,?protected?or package-visible methods can also be made transactional for class-based proxies by default. Note that transactional methods in interface-based proxies must always be?public?and defined in the proxied interface. For both kinds of proxies, only external method calls coming in through the proxy are intercepted.

Using @Transactional :: Spring Framework

示例代碼:

@Service
public class UserService {@Autowiredprivate UserRepository userRepository;@Transactionalprivate void createUser(User user) {// 插入用戶數據userRepository.save(user);// 可能會拋出異常if (user.getName().equals("error")) {throw new RuntimeException("創建用戶失敗");}}
}

因為方法的可見性,private修飾的方法不能被代理攔截

場景3、調用內部方法

@Transactional?注解使用 AOP 實現事務管理,而 AOP 是基于代理模式的。當在同一個類內部一個沒有事務注解的方法調用有?@Transactional?注解的方法時,事務注解會失效,因為這種調用沒有經過代理對象。

示例代碼如下:

// 定義 UserService 類,處理用戶相關業務邏輯
@Service
class UserService {@Autowiredprivate UserRepository userRepository;// 無事務注解的方法,內部調用有事務注解的方法public void outerMethod() {try {innerMethod();} catch (Exception e) {System.out.println("Exception caught: " + e.getMessage());}}// 有事務注解的方法@Transactionalpublic void innerMethod() {User user = new User("John");userRepository.save(user);// 模擬拋出異常throw new RuntimeException("Simulated exception");}
}

示例代碼中,雖然Spring AOP代理了innerMethod方法,但是原始事務不是通過代理的innerMethod進入,而是通過原始類的outerMethod進入,這樣就調用的是原始類的innerMethod方法,導致不能進入代理類的innerMethod方法,事務攔截不能生效。

場景4、方法內部Catch了異常

在 Spring 中使用?@Transactional?注解時,如果在方法內部捕獲了異常且沒有重新拋出,會導致事務無法正常回滾,從而使?@Transactional?注解失效。

@Transactional?注解的事務管理是基于 AOP 實現的,它會在目標方法拋出異常時進行事務回滾。默認情況下,@Transactional?注解只對未被捕獲的?RuntimeException?及其子類異常進行回滾操作。如果在方法內部捕獲了異常,Spring 就無法感知到異常的拋出,從而不會觸發事務回滾邏輯,導致事務繼續提交,@Transactional?注解的功能失效。

示例代碼

// 定義 UserService 類,處理用戶相關業務邏輯
@Service
class UserService {@Autowiredprivate UserRepository userRepository;@Transactionalpublic void createUser() {User user = new User("John");userRepository.save(user);try {// 模擬拋出異常throw new RuntimeException("Simulated exception");} catch (Exception e) {// 捕獲異常但未重新拋出System.out.println("Exception caught: " + e.getMessage());}}
}

createUser()?方法:使用?@Transactional?注解標記,在方法內部保存用戶信息后拋出一個?RuntimeException?異常,并在?catch?塊中捕獲該異常,但沒有重新拋出。

解決辦法:重新拋出異常

在?catch?塊中重新拋出異常,讓 Spring 能夠感知到異常的發生,從而觸發事務回滾邏輯。

@Transactional
public void createUser() {User user = new User("John");userRepository.save(user);try {throw new RuntimeException("Simulated exception");} catch (Exception e) {System.out.println("Exception caught: " + e.getMessage());// 重新拋出異常throw e;}
}

場景5、?rollbackFor/rollbackForClassName屬性未正確配置

  • 默認回滾規則:@Transactional注解默認只對RuntimeException及其子類和Error進行回滾。若拋出的是受檢查異常(如IOExceptionSQLException),默認不會觸發回滾。

@Service
class UserService {@Autowiredprivate UserRepository userRepository;@Transactionalpublic void createUser() throws IOException {User user = new User("John");userRepository.save(user);// 拋出受檢查異常throw new IOException("Simulated IOException");}
}

IOException是一個CheckedException的子類,不在默認的回滾體系內,所以不能自動回滾。需要使用rollbackFor屬性顯式指定才能生效。

  • rollbackFor=BaseException.class, 針對BaseException和它的子類回滾。若拋出的異常不在繼承體系內,則不能自動回滾。
    @Service
    public class OrderService {@Autowiredprivate OrderRepository orderRepository;@Transactional(rollbackFor = BaseException.class)public void createOrder(Order order) throws Exception {orderRepository.createOrder(order);// will rollback// throw new SubException("出現未知錯誤");// will not rollbackthrow new OtherException("出現未知錯誤");}
    }class BaseException extends Exception {public BaseException(String message) {super(message);}
    }class SubException extends BaseException {public SubException(String message) {super(message);}
    }class OtherException extends Exception {public OtherException(String message) {super(message);}
    }

    rollbackForClassName=exceptionPattern, exceptionPattern可以包含異常名字全部或者部分字符。注意這里不是正則表達式,而是基于String.contains(exceptionPattern)來判斷的。

    匹配規則的源碼如下:

private int getDepth(Class<?> exceptionType, int depth) {if (this.exceptionType != null) {if (this.exceptionType.equals(exceptionType)) {// Found it!return depth;}}else if (exceptionType.getName().contains(this.exceptionPattern)) {// Found it!return depth;}// If we've gone as far as we can go and haven't found it...if (exceptionType == Throwable.class) {return -1;}return getDepth(exceptionType.getSuperclass(), depth + 1);}

不能匹配的示例代碼如下:

@Slf4j
@Service
public class OrderService {@Autowiredprivate OrderRepository orderRepository;@Transactional(rollbackForClassName = "Base*Exception")public void createOrder(Order order) throws Exception {orderRepository.createOrder(order);// will not rollbackthrow new Base1Exception("出現未知錯誤");}
}

?因為exception.typeName="Base1Exception" contains("Base*Exception") 結果未false,所以不匹配,導致不能回滾。

正確的用法如下:

@Service
public class OrderService {@Autowiredprivate OrderRepository orderRepository;@Transactional(rollbackForClassName = "Base")public void createOrder(Order order) throws Exception {orderRepository.createOrder(order);// will rollbackthrow new Base1Exception("出現未知錯誤");}
}

以上是我過去經常碰到的@Transactional碰到異常不能正常回滾的案例總結,若有遺漏歡迎下方留言。

參考文檔:

Declarative Transaction Management :: Spring Framework

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

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

相關文章

React 與 Vue 虛擬 DOM 實現原理深度對比:從理論到實踐

在現代前端開發中&#xff0c;React 和 Vue 作為最流行的兩大框架&#xff0c;都采用了虛擬 DOM&#xff08;Virtual DOM&#xff09; 技術來優化渲染性能。虛擬 DOM 的核心思想是通過 JavaScript 對象模擬真實 DOM&#xff0c;減少直接操作 DOM 的開銷&#xff0c;從而提高頁面…

WordPress AI 原創文章自動生成插件 24小時全自動生成SEO原創文章 | 多語言支持 | 智能配圖與排版

為什么選擇Linkreate AI內容生成插件&#xff1f; ? 全自動化工作流程 - 從關鍵詞挖掘到文章發布一站式完成 ? 多語言支持 - 輕松覆蓋全球市場&#xff08;中/英等多語種&#xff09; ? 智能SEO優化 - 自動生成搜索引擎友好的內容結構 ? AI智能配圖 - 每篇文章自動匹配高質…

GPU加速-系統CUDA12.5-Windows10

誤區注意 查看當前系統可支持的最高版本cuda&#xff1a;nvidia-smi 說明&#xff1a; 此處顯示的12.7只是驅動對應的最高版本&#xff0c;不一定是 / 也不一定需要是 當前Python使用的版本。但我們所安裝的CUDA版本需要 小于等于它&#xff08;即≤12.7&#xff09;因此即使…

IOT項目——DIY 氣象站

開源項目&#xff1a;ESP32 氣象站 作者&#xff1a;GiovanniAggiustatutto 原文鏈接&#xff1a;原文 開源項目&#xff1a;太陽能 WiFi 氣象站 V4.0 作者&#xff1a;opengreenenergy 原文鏈接&#xff1a;原文 DIY 氣象站 簡介1-制版2-物料 溫度設備塔風向標風速計雨量計框…

5G助力智慧城市的崛起——從概念到落地的技術實踐

5G助力智慧城市的崛起——從概念到落地的技術實踐 引言&#xff1a;智慧城市中的“隱形脈絡” 隨著城市化的快速推進&#xff0c;傳統的城市管理方式已經難以滿足人口增長和資源優化的需求。智慧城市的概念應運而生&#xff0c;通過技術創新實現智能化、可持續發展的城市生態…

【Linux】web服務器的部署和優化

目錄 nginx的安裝與啟用--/usr/share/nginx/html默認發布目錄 nginx的主配置文件--/etc/nginx/nginx_conf nginx的端口 nginx默認發布文件--index.html nginx默認發布目錄 nginx的訪問控制 基于IP地址的訪問控制 基于用戶認證的訪問控制 nginx的虛擬主機--/etc/nginx/…

結合五層網絡結構講一下用戶在瀏覽器輸入一個網址并按下回車后到底發生了什么?

文章目錄 實際應用第一步&#xff1a;用戶在瀏覽器輸入 www.baidu.com 并按下回車1. 瀏覽器觸發域名解析&#xff08;DNS查詢&#xff09; 第二步&#xff1a;DNS請求的逐層封裝與傳輸1. 應用層&#xff08;DNS協議&#xff09;2. 傳輸層&#xff08;UDP協議&#xff09;3. 網絡…

深入理解N皇后問題:從DFS到對角線優化

N皇后問題是一個經典的算法問題&#xff0c;要求在NN的棋盤上放置N個皇后&#xff0c;使得它們互不攻擊。本文將全面解析該問題的解法&#xff0c;特別聚焦于DFS算法和對角線優化的數學原理。 問題描述 在NN的國際象棋棋盤上放置N個皇后&#xff0c;要求&#xff1a; 任意兩個…

Java面試場景篇:分布式鎖的實現與組件詳解

互聯網大廠Java求職者面試&#xff1a;分布式鎖的實現與組件 在一場緊張而又充滿挑戰的面試中&#xff0c;Java架構師馬架構正面對著一位經驗豐富的面試官。以下是他們之間關于分布式鎖實現方式及相關問題的對話。 第一輪提問 面試官&#xff1a;請介紹一下分布式鎖的概念。…

關于使用 讀光-文字檢測-DBNet行檢測模型-中英-通用領域,版本問題

關于使用 讀光-文字檢測-DBNet行檢測模型-中英-通用領域&#xff0c;版本問題 pip install modelscopeSuccessfully installed certifi-2025.4.26 charset-normalizer-3.4.1 colorama-0.4.6 idna-3.10 modelscope-1.25.0 requests-2.32.3 tqdm-4.67.1 urllib3-2.4.0 pip insta…

刷刷刷刷刷RCE

云曦歷年考核 25年春開學考 RCCCE 開啟題目進行代碼審計 GET傳參傳入一個參數cmd&#xff0c;但對參數內容給了黑名單進行過濾 $blacklist /bash|nc|wget|ping|ls|cat|more|less|phpinfo|base64|echo|php|python|mv|cp|la|\-|\*|"|\>|\<|\%|\$/i; ls、cat等都…

2024江西ICPC部分題解

題目列表 A - Maliang Learning PaintingC - LiarG - Multiples of 5H - ConvolutionJ - Magic MahjongK - Magic Tree A - Maliang Learning Painting 題目來源&#xff1a;A - Maliang Learning Painting 思路分析 這是個簽到題&#xff0c;直接輸出abc即可 #include<b…

Pytorch圖像數據轉為Tensor張量

PyTorch的所有模型&#xff08;nn.Module&#xff09;都只接受Tensor格式的輸入&#xff0c;所以我們在使用圖像數據集時&#xff0c;必須將圖像轉換為Tensor格式。PyTorch提供了torchvision.transforms模塊來處理圖像數據集。torchvision.transforms模塊提供了一些常用的圖像預…

為什么vllm能夠加快大模型推理速度?

vLLM加速大模型推理的核心技術原理可分解為以下關鍵創新點&#xff1a; 一、?內存管理革命&#xff1a;PagedAttention? KV Cache分頁機制? 將傳統連續存儲的KV Cache拆分為非連續內存頁&#xff0c;類似操作系統內存分頁管理&#xff0c;消除內存碎片并實現動態分配。13B…

第十一章 多態

多態是面向對象開發過程中一個非常重要的概念。 11.1 多態概述 11.1.1 什么是多態 多態&#xff08;polymorphism&#xff09;&#xff0c;從字面理解是“多種形態&#xff0c;多種形式”&#xff0c;是一種將不同的特殊行為泛化為當個特殊記號的機制。 多態從實現的角度可劃…

RNN——循環神經網絡

一.基本結構 1.目標&#xff1a;處理序列數據&#xff08;時間序列&#xff0c;文本&#xff0c;語音等&#xff09;&#xff0c;捕捉時間維度上的依賴關系 核心機制&#xff1a;通過隱藏狀態&#xff08;hidden State&#xff09;傳遞歷史信息&#xff0c;每個時間步的輸入包…

性能提升手段--池化技術

看到hadoop代碼里有ByteBufferPool,使用池子來避免頻繁創建、銷毀ByteBuffer,減輕GC壓力,提高性能。 順便總結一下池化技術 一、什么是池化技術??? ??池化(Pooling)?? 是一種資源管理策略,通過??預先創建并復用資源??(如數據庫連接、線程、內存對象等)來提…

數據安全和合規性市場分析

一、什么是數據安全和合規性 在數據安全和合規性方面&#xff0c;存在著一系列重要的法律、法規和行業標準&#xff0c;這些規定了組織如何收集、存儲、處理和保護個人數據及其他敏感信息。企業之所以要遵守這些規定&#xff0c;是出于多方面的考量&#xff0c;既有法律責任&a…

【每日八股】復習計算機網絡 Day4:TCP 協議的其他相關問題

文章目錄 昨日內容復習已經建立了 TCP 連接&#xff0c;客戶端突然出現故障怎么辦&#xff1f;什么時候用長連接&#xff1f;短連接&#xff1f;TCP 的半連接隊列與全連接隊列&#xff1f;什么是 SYN 攻擊&#xff1f;如何避免&#xff1f;TIME_WAIT 的作用&#xff1f;過多如何…

React:<></>的存在是為了什么

1. <></> 是什么&#xff1f; <></> 是 React 的Fragment&#xff08;片段&#xff09;語法糖&#xff0c;等價于 <React.Fragment></React.Fragment>。 2. 它的作用 主要作用&#xff1a; 允許你在組件里返回多個元素&#xff0c;而不需…