class ServiceA{
method_a(){ //PROPAGATION_REQUIRED
逻辑 A1{
更新表a1;
}
逻辑 A2{
ServiceB.method_b();
}
逻辑 A3{
更新表a2;
}
}
}
class ServiceB{
method_b(){
ServiceC.method_c
更新表b;
}
}
class ServiceC{
method_c(){ //PROPAGATION_REQUIRES_NEW
存储过程c(显式执行commit);
}
}
现状
ServiceA.method_a本身有事务,里面调用ServiceB.method_b,另起新事务。 执行逻辑A3时,更新表a2失败。只回滚了ServiceB里面的更新表b,无法回滚更新表a1及存储过程c。
目的
业务场景中的目的是,无论逻辑A3报什么错,都不能影响逻辑A1、A2
只要在逻辑A2后,手动提交事务即可【已解决】
疑问
逻辑A1为什么会被存储过程一起commit了,无法再回滚?
分析
Spring事务管理是通过JDK动态代理的方式进行实现的(另一种是使用CGLib动态代理实现的),也正是因为动态代理的特性造成了上述方法中的新开事务失效!只有代理对象proxy直接调用的那个方法才是真正的走代理的,嵌套的方法实际上就是 直接把嵌套的代码移动到代理的方法里面。 所以,嵌套的事务都不能生效。故调用ServiceC.method_c其实没有产生新事务,存储过程在显示执行commit时,直接提交了ServiceA.method_a的事务,后面无法再回滚。
只有来自外部的方法调用才会被AOP代理捕捉,类内部方法调用类内部的其他方法,子方法并会不引起事务行为,即使被调用的方法上使用有@Transactional注解。
类内部方法调用内部的其他方法,被调用的方法体中如果有try catch,则catch中必须用throw new Exception(),否则即使主方法上加上@Transactional注解,如果被调用的子方法出错也不会抛出异常,不会引起事务起作用。
解决方案
参考https://blog.youkuaiyun.com/xlgen157387/article/details/79026285
https://blog.youkuaiyun.com/m0_37701381/article/details/85066711
验证效果
项目中本来逻辑B的存储过程就是在另一个service类中实现的,属于调用外部方法
获取代理对象调用内嵌方法【报错aspectj-autoproxy 循环依赖报错has been injected into other beans】
从上下文对象获取对象调用 【我的项目中无效】
结论
疑问暂未解决,待后续完善。