SpringBoot 中的 @Transactional 事務(wù)基本作用及注意事項示例詳解
前言
@Transactional 是 Spring 中用于聲明式事務(wù)管理的核心注解,它可以簡化事務(wù)控制邏輯,通過注解方式替代傳統(tǒng)的編程式事務(wù)管理。在 Spring Boot 中,@Transactional 的使用尤為廣泛,以下是其詳細(xì)解析:
一、基本作用
@Transactional 用于標(biāo)記某個方法或類需要被事務(wù)管理,Spring 會在方法執(zhí)行前后自動開啟、提交或回滾事務(wù),確保操作的原子性(要么全成功,要么全失敗)。
使用位置:
- 方法上:僅對該方法生效
- 類上:對類中所有公共方法生效(public)
- 接口上:不推薦(可能因代理模式導(dǎo)致失效)
二、核心屬性
@Transactional 有多個屬性可配置,常用的包括:
| 屬性名 | 作用 | 默認(rèn)值 |
|---|---|---|
| rollbackFor | 指定哪些異常觸發(fā)回滾(類或數(shù)組) | 僅 RuntimeException 及其子類 |
| noRollbackFor | 指定哪些異常不觸發(fā)回滾 | 無 |
| isolation | 事務(wù)隔離級別(控制并發(fā)數(shù)據(jù)可見性) | Isolation.DEFAULT(數(shù)據(jù)庫默認(rèn)) |
| propagation | 事務(wù)傳播行為(控制嵌套方法的事務(wù)歸屬) | Propagation.REQUIRED |
| readOnly | 是否為只讀事務(wù)(優(yōu)化性能,適合查詢操作) | false |
| timeout | 事務(wù)超時時間(秒),超時后自動回滾 | -1(無超時) |
| value/transactionManager | 指定事務(wù)管理器(多數(shù)據(jù)源時使用) | 默認(rèn)事務(wù)管理器 |
1. 異?;貪L配置(rollbackFor/noRollbackFor)
- 默認(rèn)規(guī)則:僅對 RuntimeException 及其子類(非檢查異常)回滾,對 Exception 及其子類(檢查異常)不回滾。
- 推薦配置:rollbackFor = Exception.class,確保所有異常都觸發(fā)回滾:
示例:
@Transactional(rollbackFor = Exception.class) // 所有異常都回滾
public void saveData() throws Exception {
// 業(yè)務(wù)邏輯
}
- 特殊需求:指定不回滾的異常:
示例:
@Transactional(noRollbackFor = ArithmeticException.class)
public void process() {
// 發(fā)生 ArithmeticException 不回滾,其他異常正?;貪L
}
2. 隔離級別(isolation)
控制并發(fā)事務(wù)之間的數(shù)據(jù)可見性,可選值:
- Isolation.DEFAULT:使用數(shù)據(jù)庫默認(rèn)隔離級別(推薦)
- Isolation.READ_UNCOMMITTED:讀未提交(最低隔離,可能臟讀)
- Isolation.READ_COMMITTED:讀已提交(避免臟讀,多數(shù)數(shù)據(jù)庫默認(rèn))
- Isolation.REPEATABLE_READ:可重復(fù)讀(避免臟讀、不可重復(fù)讀,MySQL 默認(rèn))
- Isolation.SERIALIZABLE:串行化(最高隔離,避免幻讀,性能最低)
示例:
@Transactional(isolation = Isolation.READ_COMMITTED)
public void queryData() {
// 事務(wù)內(nèi)查詢,只能看到已提交的數(shù)據(jù)
}
3.臟讀(Dirty Read)
定義:一個事務(wù)讀取到了另一個未提交事務(wù)修改的數(shù)據(jù)。
場景示例:
- 事務(wù) A:更新了一條記錄但未提交
- 事務(wù) B:讀取了事務(wù) A 更新后的記錄
- 事務(wù) A:由于某種原因回滾了操作
- 結(jié)果:事務(wù) B 讀取到的數(shù)據(jù)是 “無效” 的(已被回滾)
危害:導(dǎo)致讀取到臨時且可能被撤銷的數(shù)據(jù),影響業(yè)務(wù)邏輯正確性。
避免方式:將事務(wù)隔離級別設(shè)置為 READ COMMITTED 或更高(REPEATABLE READ、SERIALIZABLE)。
MySQL 默認(rèn)隔離級別是 REPEATABLE READ,已避免臟讀。
4.幻讀(Phantom Read)
定義:一個事務(wù)在兩次查詢中得到不同的結(jié)果集,因為另一個事務(wù)在兩次查詢之間插入或刪除了數(shù)據(jù)。
場景示例:
- 事務(wù) A:查詢年齡 > 20 的用戶,得到 10 條記錄
- 事務(wù) B:插入了一條年齡 = 25 的新用戶并提交
- 事務(wù) A:再次執(zhí)行相同查詢,得到 11 條記錄(多了一條 “幻影” 數(shù)據(jù))
5. 傳播行為(propagation)
控制嵌套方法的事務(wù)歸屬,核心值:
- Propagation.REQUIRED(默認(rèn)):如果當(dāng)前有事務(wù),則加入;否則新建事務(wù)。
- Propagation.REQUIRES_NEW:無論當(dāng)前是否有事務(wù),都新建獨立事務(wù)(原事務(wù)暫停)。
- Propagation.SUPPORTS:如果當(dāng)前有事務(wù),則加入;否則以非事務(wù)方式執(zhí)行。
- Propagation.NOT_SUPPORTED:以非事務(wù)方式執(zhí)行,若當(dāng)前有事務(wù)則暫停。
- Propagation.NEVER:必須在非事務(wù)環(huán)境執(zhí)行,否則拋異常。
- Propagation.MANDATORY:必須在事務(wù)環(huán)境執(zhí)行,否則拋異常。
- Propagation.NESTED:嵌套事務(wù)(依賴數(shù)據(jù)庫支持,如 MySQL 的 SAVEPOINT)。
示例:獨立事務(wù)場景
@Service
public class OrderService {
@Autowired
private LogService logService;
@Transactional(rollbackFor = Exception.class)
public void createOrder() {
// 訂單業(yè)務(wù)(主事務(wù))
logService.saveLog(); // 調(diào)用日志服務(wù)
}
}@Service
public class LogService {
// 日志保存使用獨立事務(wù),即使訂單回滾,日志仍會提交
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public void saveLog() {
// 保存日志
}
}6. 其他屬性
- readOnly = true:適用于純查詢方法,數(shù)據(jù)庫可優(yōu)化性能(如避免寫鎖):
@Transactional(readOnly = true)
public List<User> queryUsers() {
return userMapper.selectAll();
}- timeout = 10:事務(wù)超時時間 10 秒,超時后自動回滾:
@Transactional(timeout = 10)
public void longTimeTask() {
// 長時間任務(wù),超時會回滾
}
三、事務(wù)生效條件
1.代理模式:Spring 事務(wù)基于 AOP 代理實現(xiàn),需滿足:
- 方法必須是 public(非 public 方法注解可能失效)。
- 需通過 Spring 容器的代理對象調(diào)用方法(同類中自調(diào)用可能失效,需特殊處理)。
2.異常處理:
- 異常必須拋出到 @Transactional 標(biāo)記的方法外(被捕獲的異常不會觸發(fā)回滾)。
- 需符合 rollbackFor 配置的異常類型。
3.數(shù)據(jù)庫支持:
- 數(shù)據(jù)庫必須支持事務(wù)(如 MySQL 的 InnoDB 引擎,MyISAM 不支持)。
四、常見問題
1.自調(diào)用事務(wù)失效:
@Service
public class UserService {
public void methodA() {
methodB(); // 自調(diào)用,事務(wù)可能失效
}
@Transactional
public void methodB() {
// 業(yè)務(wù)邏輯
}
}解決:通過 Spring 上下文獲取代理對象調(diào)用,或使用 @EnableAspectJAutoProxy(exposeProxy = true) 暴露代理。
2.多線程事務(wù)不共享:
新線程中的方法不會加入當(dāng)前事務(wù),需手動協(xié)調(diào)事務(wù)。
3.錯誤的異常處理:
@Transactional
public void save() {
try {
// 業(yè)務(wù)邏輯
} catch (Exception e) {
// 異常被捕獲,未拋出,事務(wù)不會回滾
}
}解決:捕獲后需重新拋出異常,或手動調(diào)用 TransactionStatus.setRollbackOnly()。
五、總結(jié)
@Transactional 是 Spring Boot 中簡化事務(wù)管理的強大工具,核心關(guān)注:
- 異?;貪L規(guī)則(rollbackFor)
- 并發(fā)控制(isolation)
- 嵌套事務(wù)關(guān)系(propagation)
合理配置可確保數(shù)據(jù)一致性,同時需注意代理模式、異常處理等細(xì)節(jié)避免事務(wù)失效。
到此這篇關(guān)于SpringBoot 中的 @Transactional 事務(wù)詳解及注意事項的文章就介紹到這了,更多相關(guān)SpringBoot @Transactional 事務(wù)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Springboot ApplicationRunner的使用解讀
這篇文章主要介紹了Springboot ApplicationRunner的使用解讀,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-05-05
java servlet手機app訪問接口(一)數(shù)據(jù)加密傳輸驗證
這篇文章主要為大家詳細(xì)介紹了java servlet手機app訪問接口(一),數(shù)據(jù)加密傳輸驗證,具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-12-12
Java使用ProcessBuilder?API優(yōu)化流程
Java?的?Process?API?為開發(fā)者提供了執(zhí)行操作系統(tǒng)命令的強大功能,這篇文章將詳細(xì)介紹如何使用?ProcessBuilder?API?來方便的操作系統(tǒng)命令,需要的可以收藏一下2023-06-06
java使用JDBC動態(tài)創(chuàng)建數(shù)據(jù)表及SQL預(yù)處理的方法
這篇文章主要介紹了java使用JDBC動態(tài)創(chuàng)建數(shù)據(jù)表及SQL預(yù)處理的方法,涉及JDBC操作數(shù)據(jù)庫的連接、創(chuàng)建表、添加數(shù)據(jù)、查詢等相關(guān)實現(xiàn)技巧,需要的朋友可以參考下2017-08-08
Java去重排序之Comparable與Comparator的使用及說明
這篇文章主要介紹了Java去重排序之Comparable與Comparator的使用及說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-04-04
淺談java分頁三個類 PageBean ResponseUtil StringUtil
下面小編就為大家?guī)硪黄獪\談java分頁三個類 PageBean ResponseUtil StringUtil。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-07-07
java從命令行獲取數(shù)據(jù)的三種方式代碼實例
這篇文章主要介紹了java從命令行獲取數(shù)據(jù)的三種方式代碼實例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2019-12-12

