詳解Spring中REQUIRED事務(wù)的回滾機(jī)制詳解
在 Spring 的事務(wù)管理中,REQUIRED 是最常用也是默認(rèn)的事務(wù)傳播屬性。很多開發(fā)者在使用時(shí)會(huì)遇到一個(gè)常見的困惑:為什么內(nèi)部方法拋出的異常即便被外層捕獲了,事務(wù)還是會(huì)整體回滾? 本文將深入剖析 REQUIRED 下的事務(wù)回滾機(jī)制及其背后的原理。
1. REQUIRED 的定義
- 默認(rèn)傳播屬性。
- 如果當(dāng)前存在事務(wù):加入當(dāng)前事務(wù)。
- 如果沒有事務(wù):創(chuàng)建一個(gè)新事務(wù)。
這意味著調(diào)用鏈上的所有方法都運(yùn)行在同一個(gè)物理事務(wù)里,彼此之間沒有隔離。只要其中一個(gè)方法觸發(fā)回滾條件,整個(gè)事務(wù)都會(huì)被撤銷。
2. REQUIRED 下的回滾機(jī)制
2.1 異常觸發(fā)回滾
Spring 默認(rèn)回滾規(guī)則:
- 運(yùn)行時(shí)異常(
RuntimeException)和錯(cuò)誤(Error):觸發(fā)回滾。 - 受檢異常(
CheckedException):不會(huì)觸發(fā)回滾,除非在@Transactional(rollbackFor = Exception.class)中顯式指定。
2.2 回滾狀態(tài)傳播
假設(shè)調(diào)用鏈如下:
@Transactional(propagation = Propagation.REQUIRED)
public void outer() {
try {
inner();
} catch (Exception e) {
// 異常被捕獲
}
}
@Transactional(propagation = Propagation.REQUIRED)
public void inner() {
throw new RuntimeException("內(nèi)部錯(cuò)誤");
}
執(zhí)行過程:
outer()啟動(dòng)時(shí)創(chuàng)建事務(wù)。inner()加入同一事務(wù)并拋出異常。- Spring 的事務(wù)攔截器捕獲異常 → 標(biāo)記當(dāng)前事務(wù)為 rollback-only。
- 即便外層
outer()捕獲了異常,rollback-only 標(biāo)記不會(huì)自動(dòng)清除。 - 最終事務(wù)提交時(shí),Spring 檢查到 rollback-only → 調(diào)用
rollback()而不是commit()。
因此,捕獲異常并不能阻止事務(wù)回滾。
3. 為什么捕獲異常也會(huì)回滾?
原因在于 Spring 的事務(wù)是基于 事務(wù)狀態(tài)標(biāo)記 而不是你的 try-catch 來決定的:
- 方法拋出異常 →
TransactionInterceptor判斷是否需要回滾。 - 如果符合規(guī)則 →
status.setRollbackOnly()。 - 外層即便捕獲異常,也只是業(yè)務(wù)層邏輯“消化”了異常,但事務(wù)狀態(tài)已經(jīng)不可提交。
最終由事務(wù)管理器在 commit() 階段檢查 rollback-only 標(biāo)記,決定整體回滾。
4. 如何避免“誤回滾”?
如果確實(shí)需要內(nèi)部異常不影響外部事務(wù),可以考慮以下方式:
4.1 修改回滾規(guī)則
@Transactional(noRollbackFor = RuntimeException.class)
public void inner() {
throw new RuntimeException("不會(huì)觸發(fā)回滾");
}
但這種方式風(fēng)險(xiǎn)極高,可能提交錯(cuò)誤數(shù)據(jù)。
4.2 手動(dòng)清理事務(wù)狀態(tài)(不推薦)
catch (Exception e) {
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(false);
}
此方式非常危險(xiǎn),通常不建議。
4.3 使用其他傳播屬性(推薦)
- REQUIRES_NEW:為內(nèi)部方法開啟新事務(wù),失敗只影響內(nèi)部,不影響外部。
- NESTED:使用保存點(diǎn),內(nèi)部失敗可回滾到保存點(diǎn),外部事務(wù)繼續(xù)。
這兩種方式才是更優(yōu)雅的解決方案。
5. Spring 內(nèi)部執(zhí)行流程
核心邏輯在 TransactionAspectSupport.invokeWithinTransaction():
- 獲取或創(chuàng)建事務(wù)。
- 執(zhí)行目標(biāo)方法。
- 捕獲異常 → 判斷是否需要回滾 → 設(shè)置 rollback-only。
- 方法結(jié)束 → 根據(jù)狀態(tài)決定
commit()還是rollback()。
6. 總結(jié)
REQUIRED下,所有方法共享一個(gè)事務(wù)。- 任意方法異常觸發(fā) rollback-only → 整體回滾。
- 捕獲異常≠事務(wù)安全,Spring 根據(jù)事務(wù)狀態(tài)決定提交還是回滾。
- 如果需要更細(xì)粒度的控制,應(yīng)使用
REQUIRES_NEW或NESTED。
一句話總結(jié):在 Spring 的 REQUIRED 模式下,事務(wù)是一榮俱榮、一損俱損的整體,內(nèi)部異常即便被捕獲也會(huì)讓整個(gè)事務(wù)回滾,真正想做到“局部回滾”需要換傳播屬性來實(shí)現(xiàn)。
到此這篇關(guān)于詳解Spring中REQUIRED事務(wù)的回滾機(jī)制詳解的文章就介紹到這了,更多相關(guān)Spring REQUIRED 事務(wù)回滾內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
idea數(shù)據(jù)庫驅(qū)動(dòng)下載失敗的問題及解決
這篇文章主要介紹了idea數(shù)據(jù)庫驅(qū)動(dòng)下載失敗的問題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01
@Slf4j?如何實(shí)現(xiàn)日志輸入到外部文件
這篇文章主要介紹了@Slf4j?如何實(shí)現(xiàn)日志輸入到外部文件,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12
SpringBoot整合Druid并開啟監(jiān)控過程
Druid高性能數(shù)據(jù)庫連接池與SpringBoot結(jié)合使用,顯著提升數(shù)據(jù)訪問性能,通過內(nèi)置監(jiān)控頁面實(shí)時(shí)查看連接狀態(tài)、SQL執(zhí)行情況等關(guān)鍵信息2026-06-06
Java?ArrayList遍歷foreach與iterator時(shí)remove的區(qū)別
這篇文章主要介紹了Java?ArrayList遍歷foreach與iterator時(shí)remove的區(qū)別,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的朋友可以參考一下2022-07-07
SpringCloud整合Netty集群實(shí)現(xiàn)WebSocket的示例代碼
文章主要介紹了SpringCloud整合Netty集群實(shí)現(xiàn)WebSocket的相關(guān)內(nèi)容,包括服務(wù)注冊(cè)和發(fā)現(xiàn)中心的配置,如使用Nacos、CommandLineRunner啟動(dòng)Netty服務(wù)等,還介紹了通過Redis實(shí)現(xiàn)消息發(fā)布訂閱的機(jī)制,需要的朋友可以參考下2024-11-11
Java實(shí)現(xiàn)線程按序交替執(zhí)行的方法詳解
這篇文章主要為大家詳細(xì)介紹了Java如何實(shí)現(xiàn)線程按序交替執(zhí)行,文中的示例代碼講解詳細(xì),對(duì)我們了解線程有一定幫助,需要的可以參考一下2022-10-10

