Transaction rolled back because it has been marked as rollback-only

Spring事务管理报错:Transaction rolled back because it has been marked as rollback-only

出错代码

算是比较常见的错误了

很多解释说明都云里雾里的

其实比较简单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class ServiceA{

@Autowired
ServiceB serviceB;

@Transactional(rollbackFor = Exception.class)
public void testA(){
try{
serviceB.testB();
}catch(Exception e){

}
}//method
}//class

class ServiceB{
@Transactional(rollbackFor = Exception.class)
public void testB(){
throw new Exception("测试");
}//method
}//class

上面这段代码就会报错 Transaction rolled back because it has been marked as rollback-only

原因是 Spring 事务管理是通过AOP切入的

ServiceA 的 testA 已经切入

testB 又切入了一次

@Transactional 的默认传播机制是 Propagation propagation() default Propagation.REQUIRED;

等于 testA 和 testB 使用的是同一个事务

testB 抛出异常时 Spring Aop 已经把事务标记为需要回滚

testA 使用同一个事务 try-catch 了 testB 的异常 所以 testA 没有异常 方法结束

testA 提交事务时 由于 testB已经被回滚 所以抛异常

原因

AOP.serviceA.testA -> 进入方法前 开启事务

serviceA.testA -> 方法执行

AOP.serivceB.testB -> 进入方法前 开启事务 读取到已有事务 REQUIRED 继承A的事务

serviceB.testB -> 方法执行 出现异常

AOP.serviceB.testB -> 方法执行后 检测到异常 回滚事务

serviceA.testA -> 方法执行 testB的异常被 try-catch 方法正常结束

AOP.serviceA.testA -> 方法执行后 没有异常 提交事务

其实就是 AOP 切入了两次 A 和 B 使用的是同一个事务

并且 抓取 B 的错误时 B 已经回滚了事务

解决方法

  1. 指定 testB 的事务传播性为 REQUIRES_NEW (testB单独一个事务 回滚不影响 testA
  2. 把 testA 中的 try-catch 放到 testB 中 testB返回true false 给 testA 判断
  3. 从 controller 中分别调用 testA 和 testB