为什么@Transactional不生效?Spring事务失效的排查手册

本文系统解析Spring中@Transactional
事务失效的13类常见原因(如代理机制限制、异常处理不当、配置缺陷等),并提供针对性解决方案,助力开发者高效避坑。
博客:https://www.emanjusaka.com
博客园:https://www.cnblogs.com/emanjusaka
公众号:emanjusaka的编程栈
by emanjusaka from https://www.emanjusaka.com/archives/spring-transactional-invalid 彼岸花开可奈何
本文为原创文章,可能会更新知识点以及修正文中的一些错误,全文转载请保留原文地址,避免产生因未即时修正导致的误导。
在 Java 中,事务管理通常使用 Spring 框架提供的事务管理功能。事务不生效可能由多种原因导致。
下面介绍一些常见的情况:
- 方法访问权限问题
- 自调用问题
- 异常处理不当
- 事务传播设置不当
- 没有启用事务管理
- 数据库引擎不支持事务
- 在非 Spring 管理的类中使用事务注解
- 事务方法被 final 或 static 修饰
- 使用了错误的注解
- 事务超时或只读设置不当
- 多个事务管理器的情况下未指定事务管理器
- 嵌套事务传播行为设置不当
- 在异步方法中事务上下文丢失
方法访问权限问题
Spring 默认使用 AOP 代理,对于非 public 方法,事务可能不会生效(因为 Spring 的事务拦截器只能拦截 public 方法)。除非使用 AspectJ 模式。
@Transactional
private void updateData() { // 非 public 方法,事务失效!
// ...
}
自调用问题
在同一个类中,一个非事务方法调用另一个事务方法,事务不会生效。这是因为事务是通过代理对象实现的,自调用不会经过代理对象。
public void outerMethod() {
innerMethod(); // 自调用,事务不生效!
}
@Transactional
public void innerMethod() {
// 数据库操作
}
异常处理不当
如果在事务方法中捕获了异常,而没有重新抛出,则事务不会回滚。因为事务管理器只有检测到未处理的异常时才会回滚。
-
捕获异常未重新抛出
@Transactional public void update() { try { // 数据库操作(抛出异常) } catch (Exception e) { // 捕获后未抛出,事务不回滚! } }
-
默认只对
RuntimeException
和Error
回滚,检查异常不回滚。@Transactional public void update() throws IOException { // 检查异常 // ... throw new IOException(); // 事务不回滚! }
一般会明确指定回滚异常类型
@Transactional(rollbackFor = Exception.class)
事务传播设置不当
如果内部方法的事务传播行为设置为Propagation.NOT_SUPPORTED
,则它会以非事务方式执行。
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void noTransactionMethod() {
// 此方法无事务
}
没有启用事务管理
在配置类中没有使用@EnableTransactionManagement
注解(对于Java配置)或没有配置事务管理器(如PlatformTransactionManager
)。
- Spring Boot场景:
若使用Spring Boot + 数据源(如DataSourceTransactionManager
),默认会自动配置事务管理器,无需显式声明PlatformTransactionManager
Bean。
- 传统Spring配置:
- 使用
@Transactional
时需至少有一个PlatformTransactionManager
Bean存在(如DataSourceTransactionManager
、JpaTransactionManager
等)。 - 可通过
@EnableTransactionManagement
激活事务管理,Spring会自动选择容器中的唯一事务管理器。
- 使用
- 多数据源场景:
需显式定义多个PlatformTransactionManager
Bean,并通过@Transactional(transactionManager="名称")
指定。
数据库引擎不支持事务
例如,MySQL的MyISAM引擎不支持事务,而InnoDB支持。确保数据库表使用的是支持事务的引擎。
在非Spring管理的类中使用事务注解
只有Spring容器管理的Bean上的事务注解才会被处理。
事务方法被final或static修饰
因为Spring事务使用动态代理(JDK动态代理或CGLIB),对于final或static方法,代理无法重写这些方法,因此事务不会生效。
@Transactional
public final void finalMethod() { // 事务失效!
// ...
}
使用了错误的注解
错误地使用了JPA的@Transactional
而不是Spring的@Transactional
(尽管通常可以混用,但要注意包名:javax.transaction.Transactional
和org.springframework.transaction.annotation.Transactional
)。
事务超时或只读设置不当
虽然不会导致事务完全不生效,但可能导致不符合预期的行为。
多个事务管理器的情况下未指定事务管理器
如果配置了多个事务管理器,需要在@Transactional
注解中指定使用哪个事务管理器(通过transactionManager
属性)。
@Transactional(transactionManager = "otherTxManager")
嵌套事务传播行为设置不当
在嵌套事务中,内部事务设置了REQUIRES_NEW
,但外部事务捕获了内部事务的异常,导致内部事务回滚而外部事务提交。
@Transactional
public void outer() {
inner(); // 内层事务独立提交
// 即使 inner() 失败,outer() 仍可能提交
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void inner() {
// 操作数据库
}
在异步方法中事务上下文丢失
在异步方法中,事务上下文可能不会传递,需要额外的配置。
@Transactional
public void asyncUpdate() {
new Thread(() -> {
// 此处无事务控制!
}).start();
}
可以使用 Spring 的 @Async
代替手动线程。