事务失效的场景
同类内部 this 调用(最常见)
@Service
public class OrderService {
public void createOrder() {
// this.xxx() 绕过了代理,@Transactional 形同虚设
this.saveOrder();
}
@Transactional
public void saveOrder() {
// ...
}
}
调用链:外部 → Proxy.createOrder() → this.saveOrder()(真实对象,非代理)→ 事务未开启
private 方法加@Transactional 注解
@Transactional // ❌ 无意义,但不会报错,只是被忽略
private void doSomething() { ... }
这里事务失效的主要原因还是要看 Spring 实现事务的原理,是通过 AOP 来实现的,而 AOP 是通过代理来做的
- JDK: 动态代理是代理接口方法,接口没有 private 方法
- CGLIB: 是通过继承生成子类覆盖父类方法,但 private 方法无法被子类覆盖
方法不是 public(CGLIB 限制)
@Transactional
protected void doSomething() { ... } // protected/default 在某些配置下也可能失效
@Transactional
void packageMethod() { ... } // 包级私有
@Transactional 所在类未被 Spring 管理
// 没有 @Service / @Component,不是 Spring Bean
public class OrderService {
@Transactional
public void save() { ... } // ❌ 无代理对象,注解无效
}
异常类型不匹配
@Transactional // 默认只回滚 RuntimeException 和 Error
public void save() throws Exception {
throw new Exception("checked exception"); // ❌ 不会回滚,事务提交了
}
// 正确写法
@Transactional(rollbackFor = Exception.class)
public void save() throws Exception {
throw new Exception("checked exception"); // ✅ 会回滚
}
异常被 catch 吞掉,未重新抛出
@Transactional
public void save() {
try {
// ...
dao.insert();
} catch (Exception e) {
log.error("error", e);
// ❌ 没有 throw e,Spring 认为方法正常返回,提交事务
}
}
Spring 的事务拦截器在方法返回时判断是否有异常抛出,若没有异常则提交。catch 后不重抛 = 事务拦截器看不到异常 = 提交
事务传播机制设置错误
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void save() {
// 即使外层有事务,这里也以非事务方式运行
}
@Transactional(propagation = Propagation.NEVER)
public void save() {
// 外层有事务时直接抛异常
}
多线程跨线程调用
@Transactional
public void save() {
new Thread(() -> {
// ❌ 新线程没有父线程的事务上下文(ThreadLocal 不跨线程传递)
dao.insert();
}).start();
}
这是最隐蔽的失效场景。事务通过 TransactionSynchronizationManager(本质是 ThreadLocal<Map>)绑定到当前线程。新线程没有这个 ThreadLocal 值,所有数据库操作以自动提交模式执行。
这也正是 OkHttp onFailure/onResponse 回调中不能直接写 @Transactional 的根本原因——它们运行在 OkHttp 的线程池线程,不是 Spring 管理的线程,ThreadLocal 事务上下文为空。但调用一个 Spring Bean 的 @Transactional 方法是可以的,因为 Spring Proxy 会在 OkHttp 的线程上新建事务并绑定到该线程的 ThreadLocal
数据库存储引擎不支持事务提交
@Transactional 和 @Async 一起使用
@Async
@Transactional
public void asyncSave() {
// 异步执行时,@Transactional 能生效吗?
}
结论:可以生效,但是有顺序陷阱
@Async 会让方法在线程池线程上执行,Spring 代理需要在那个线程上正确建立事务上下文。实际上两者可以共存,但要注意:若 @Async 的代理先执行,调用的是真实对象还是代理对象,取决于 Spring 内部 Advisor 的排序。在 Spring Boot 中通常能正确工作,但建议分离职责——@Async 方法调用另一个 @Transactional 方法(通过代理),而不是叠加在同一个方法上。