前言:有时候在编写代码过程中,遇到事物不生效的问题,特此总结一下
代码如下
public class DemoTransactional {
public void demoTestA() {
// sql执行逻辑 。。。
demoTestB();
}
@Transactional
public void demoTestB() {
// sql执行逻辑 。。。
}
}
测试结果如下:
- 经过测试发现,当
demoTestA
方法调用同类中带有@Transactional
注解的demoTestB
方法时,被@Transactional
注解的demoTestB方法
的事务是不起作用的
原因如下:
Spring采用动态代理(AOP)实现对bean的管理和切片,它为我们的每个class生成一个代理对象。只有在代理对象之间进行调用时,可以触发切面逻辑。
而在同一个class中,demoTestA
调用方法demoTestB
,调用的是原对象的方法,而不通过代理对象。所以Spring无法切到这次调用,也就无法通过注解保证事务性了。
也就是说,在同一个类中的方法调用,则不会被方法拦截器拦截到,因此事务不会起作用。
另一种理解
Spring在处理@Tranasctional注解时,会“proxy”当前的类。如果A和B两个类都有@Transactional时,实际上运行的是A的代理类A‘, A,B的代理类B', B四个类的instance。一个外部服务调用A,实际上是 外部-->A'-->A-->B'-->B这样执行的。而抛出异常的代码实际上是在B‘做的。但是如果是同一个类内部方法直接调用的话,那么就是简单的方法直接调用,即 外部-->A'-->A方法1-->A方法2。 A方法1不会找到A'去调用。于是,“传播”的规则不会生效。
解决方法
- 将带有@Transactional注解的方法移到另一个类中,发起类之间的方法调用。
- 在第一个方法中也添加@Transactional注解。
经过测试的情况总结
前提:demoTestA
方法调用demoTestB
方法时,demoTestB
方法有多个修改SQL
测试结果
-
demoTestA
方法没开启事务,demoTestB
方法开启事务:demoTestA
和demoTestB
在同一类中,事务无效;demoTestA
和demoTestB
不在同一类中,事务生效。 -
demoTestA
方法开启事务,demoTestB
方法没开启事务:demoTestA
和demoTestB
在同一类中,事务生效;demoTestA
和demoTestB
不在同一类中,事务生效。