在 Spring Boot 項目開發中,聲明式事務管理通過 @Transactional
注解提供了極大的便利。
但許多開發者都曾遇到過事務不生效的困擾。
本文將詳細分析導致 Spring Boot 事務失效的八大常見情況,并提供相應的解決方案。
1. 數據庫引擎不支持事務
問題分析:這是最根本的原因。如果數據庫存儲引擎本身不支持事務(如 MySQL 的 MyISAM),那么無論 Spring 如何配置,事務都不會生效。
解決方案:
- 確保你的數據庫表使用支持事務的引擎,如 MySQL 的 InnoDB
- 建表時指定存儲引擎:
CREATE TABLE ... ENGINE=InnoDB;
2. 事務方法非 public 修飾
問題分析:@Transactional
基于 Spring AOP 實現,而 AOP 代理默認無法攔截非 public 方法。
@Service
public class UserService {@Transactional // 失效!private void createUser(User user) {userMapper.insert(user);}
}
解決方案:
- 始終將
@Transactional
注解應用于 public 方法上
3. 自調用問題(Within-Class Invocation)
問題分析:這是最常見且最容易踩坑的原因。當一個類中的非事務方法調用同一個類中的 @Transactional
方法時,事務不會生效。
@Service
public class UserService {public void createUser(User user) {// 一些非事務操作this.insertUser(user); // 自調用,事務失效!}@Transactionalpublic void insertUser(User user) {userMapper.insert(user);// 如果這里出現異常,事務不會回滾}
}
解決方案:
- 推薦方案:將事務方法抽取到另一個 Service 類中
- 通過 ApplicationContext 獲取代理對象調用(不優雅)
- 使用 AspectJ 模式的事務管理(配置復雜)
4. 異常類型不正確或被捕獲
問題分析:@Transactional
默認只在拋出運行時異常(RuntimeException)和 Error 時回滾。
@Transactional
public void method() throws Exception {// 數據庫操作throw new Exception("受檢異常"); // 事務不會回滾!
}
另一個陷阱:異常被捕獲但未重新拋出
@Transactional
public void method() {try {int i = 1 / 0; // 拋出 RuntimeException} catch (Exception e) {e.printStackTrace(); // 只是打印,事務不會回滾!}
}
解決方案:
- 使用
rollbackFor
屬性指定回滾的異常類型:
@Transactional(rollbackFor = Exception.class)
- 在 catch 塊中重新拋出運行時異常
5. 傳播行為(Propagation)設置不當
問題分析:錯誤配置傳播行為會導致意外結果。
@Transactional(propagation = Propagation.NOT_SUPPORTED) // 不以事務運行
public void method() {// 這里沒有事務
}
解決方案:
- 根據業務需求正確配置傳播行為
- 理解各種傳播行為的含義:
REQUIRED
(默認):支持當前事務,不存在則新建REQUIRES_NEW
:新建事務,掛起當前事務NOT_SUPPORTED
:非事務方式執行NEVER
:非事務方式執行,存在事務則拋出異常
6. 未被 Spring 容器管理
問題分析:如果類沒有加上 @Service
, @Component
等注解,它就不是 Spring Bean,其上的 @Transactional
注解不會被掃描。
// 缺少 @Service 注解
public class UserService {@Transactional // 失效!public void createUser(User user) {// ...}
}
解決方案:
- 確保類被 Spring 管理,添加適當的注解
7. 多線程環境下調用
問題分析:事務信息存儲在 ThreadLocal 中,新線程中的操作不屬于原事務。
@Transactional
public void method() {new Thread(() -> {userMapper.insert(user); // 不在事務中!}).start();
}
解決方案:
- 避免在事務方法中創建異步線程進行數據庫操作
- 使用分布式事務解決方案處理跨線程事務
8. 配置問題
問題分析:
- 未開啟事務管理(雖然 Spring Boot 會自動配置)
- 多數據源未正確配置事務管理器
解決方案:
- 確保配置了
@EnableTransactionManagement
(Spring Boot 通常自動配置) - 多數據源時為每個數據源配置對應的事務管理器:
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {return new DataSourceTransactionManager(dataSource);
}
- 使用
@Transactional(value = "txManagerName")
指定事務管理器
事務調試技巧
開啟事務調試日志,在 application.properties
中添加:
logging.level.org.springframework.transaction.interceptor=TRACE
logging.level.org.springframework.jdbc.datasource.DataSourceTransactionManager=DEBUG
這將幫助你看清事務何時開啟、回滾或提交。
總結
Spring Boot 事務失效通常由以上八大原因導致,其中自調用問題最為常見。要確保事務正常工作,需要:
- 使用支持事務的數據庫引擎(如 InnoDB)
- 將
@Transactional
應用于 public 方法 - 避免自調用,將事務方法放在不同類中
- 正確處理異常,確保異常能夠觸發回滾
- 正確配置傳播行為
- 確保 Bean 被 Spring 管理
- 避免多線程事務問題
- 檢查事務相關配置
希望本文能幫助你有效解決 Spring Boot 中的事務失效問題。