本地事务失效的常见情况主要有以下几种:
-
非 public 方法
Spring 默认使用 AOP 代理,@Transactional
注解在非 public 方法上不生效。需确保事务方法为 public
。
-
自调用问题
同一类中的非事务方法调用事务方法(如 a()
调用 @Transactional
的 b()
),因代理对象被绕过,事务不生效。应通过代理对象调用或拆分类。
-
异常处理不当
- 未抛出异常:默认事务回滚仅对
RuntimeException
和 Error
生效。若抛出检查异常(如 IOException
),需通过 @Transactional(rollbackFor = ...)
显式配置。
- 异常被捕获:在方法内
try-catch
异常但未重新抛出,事务管理器无法感知异常,不会回滚。
-
传播行为配置错误
若事务传播行为设置为 SUPPORTS
、NOT_SUPPORTED
或 NEVER
,且当前无事务上下文,方法将以非事务方式运行。
-
数据库引擎不支持事务
如 MySQL 的 MyISAM 引擎不支持事务,需使用 InnoDB。
-
未配置事务管理器
Spring 容器中未声明 PlatformTransactionManager
Bean,导致事务注解失效。
-
数据源自动提交未关闭
若数据源配置为自动提交(auto-commit=true
),每个 SQL 单独提交,事务无法回滚。
-
多线程环境
事务上下文通常绑定于线程,跨线程操作会导致事务传播失效。
-
未通过 Spring 管理
调用未被 Spring 代理的类中的 @Transactional
方法(如直接 new
对象),事务不生效。
-
代理模式限制
使用 JDK 动态代理时,非接口方法的事务可能失效,需改用 CGLIB 代理(通过 @EnableTransactionManagement(proxyTargetClass = true)
)。
总结示例
// 自调用问题示例
public class ServiceA {
public void methodA() {
methodB(); // 自调用,事务失效
}
@Transactional
public void methodB() { /* 操作数据库 */ }
}
// 异常处理不当示例
@Transactional
public void method() {
try {
// 数据库操作
} catch (Exception e) {
// 未抛出异常,事务不回滚
}
}
解决方案:
- 确保事务方法为
public
。
- 避免自调用,或通过
AopContext.currentProxy()
获取代理对象。
- 合理配置
rollbackFor
和传播行为。
- 检查数据库引擎及事务管理器配置。
- 关闭数据源自动提交,统一由事务管理。