Spring 事务踩坑记录

Spring 事务踩坑记录

环境

  • JDK 1.8
  • Spring FrameWork 4.2.5

问题简述

有 service 方法如下,doSync()doSyncSingle()@Transcational注解。doSync()doSyncSingle()的包装,每一个doSyncSingle()单独作为一个原子化操作。

1
2
3
4
5
6
7
8
9
@Transactional(rollbackFor = Exception.class)
public void doSync(){
// ...
doSyncSingle();
}
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
public void doSyncSingle(){
// ...
}

当实际运行时观察数据库发现,发生异常时事务并没有回滚,出现了状态不一致的情况。

排查后发现,doSyncSingle()并没有受到事务控制。

@Transcational注解在运行时发生了什么

Spring 的 @Transactional注解实际上是通过 AOP 代理的。

如果doSyncSingle()是在同一个类中的另一个方法doSync()用的,此时并没有触发 AOP,那么事务不会生效。

因为 Spring 的代理机制是基于类的代理,而不是基于实例的代理。当在同一个类中调用带有@Transactional注解的方法doSyncSingle()时,实际上是通过this调用的,而不是通过代理对象调用的,因此事务注解不会生效。

可能的解决方案

doSyncSingle()移动到另一个 Service

创建一个新的 Service 文件,将doSyncSingle()移动到新文件中,使用该Service去调用它。

使用当前代理对象来调用doSyncSingle()

使用AopContext.curentProxy()获取当前代理对象,然后使用它返回的代理对象去调用doSyncSingle()`

1
2
3
4
5
@Override
public void doSync() {
Service proxy = (Service) AopContext.currentProxy();
proxy.doSyncSingle();
}

总结

使用 Spring 事务时需要注意,通过 AOP 代理的类去调用。直接在同一个类中调用会绕过 AOP 代理,出现事务失效的情况。