在 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 中的事务失效问题。