Spring事务的嵌套的详细理解,以及事务失效的场景解惑

1年前 (2022) 程序员胖胖胖虎阿
152 0 0

Spring事务的的详细理解,事务嵌套解析,以及事务失效的场景解惑

想要了解Spring的事务嵌套,我们先了解一下Spring的七种事务传播属性各自表示的意思

1、propagation_requierd:如果当前没有事务,就新建一个事务,如果已存在一个事务中,加入到这个事务中,这是最常见的选择。
2、propagation_supports:支持当前事务,如果没有当前事务,就以非事务方法执行。
3、propagation_mandatory:使用当前事务,如果没有当前事务,就抛出异常。
4、propagation_required_new:新建事务,如果当前存在事务,把当前事务挂起。
5、propagation_not_supported:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
6、propagation_never:以非事务方式执行操作,如果当前事务存在则抛出异常。
7、propagation_nested:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与propagation_required类似的操作

了解了这七种传播属性表示的意思,接下来在说具体的解释

详解Spring的事务传播属性以及在写代码的过程中发生嵌套并发生事务失效的场景

再说这些之前,大家先要清除一个问题,Spring的事务是怎么实现的?
Spring本身是没有事务的,只有数据库才会回有事务,而Spring的事务是借助AOP,通过动态代理的方式,在我们要操作数据库的是时候,实际是Spring通过动态代理进行功能扩展,在我们的代码操作数据库之前通过数据库客户端打开数据库事务,如果代码执行完毕没有异常信息或者是没有Spring要捕获的异常信息时,再通过数据库客户端程序提交事务,如果有异常信息或者是有Spring要捕获的异常信息,再通过数据库客户端程序回滚事务,从而达到控制数据库事务的目的。

1、Spring的事务的简单原理示意图,请看下图
Spring事务的嵌套的详细理解,以及事务失效的场景解惑
2、Spring的Transactional注解默认情况下的传播属性是什么呢,请看一下Transactional的源码:下图所示(所以在默认的情况下我们加了Transactional 就会有事务)
Spring事务的嵌套的详细理解,以及事务失效的场景解惑
3、第1、第2两部分看明白之后,再来说一下Spring事务嵌套会发生的情况
3.1、在同一个类中发生事务嵌套的情况
Spring事务的嵌套的详细理解,以及事务失效的场景解惑
答案是会回滚的,原因是什么呢?接着看下边额示意图
Spring事务的嵌套的详细理解,以及事务失效的场景解惑
看上边的示意图你一定会明白了吧,原因还是因为代理的时候,直接把没有事务的方法包在了有事务的代理方法里边了自然就有事务了,这样说够明确吗?不够的话可以留言,给你解答一下

3.2 再看一种嵌套的方式
Spring事务的嵌套的详细理解,以及事务失效的场景解惑
答案:这时候调用test02,test01有异常发生时是不会回滚的
为什么呢?其实如果上边的你理解了下边就很容易理解了
Spring事务的嵌套的详细理解,以及事务失效的场景解惑
3.3 这些明白了 后边的就更容易明白了,再看一个例子
Spring事务的嵌套的详细理解,以及事务失效的场景解惑
上述3.3中的例子,如果调用test02 的话,当程序发生异常,test01会不会回滚呢?

答案是会回滚的 原因和3.1中的情况类似

所以综合上边说的,在同一个类中事务嵌套的话,最终的结果应该是取决于最外层的方法事务的传播特性

4、以上是在一个相同的类中的情况,如果是不同的类中的方法嵌套调用是什么样子的呢?
4.1 我们来看下列一种情况
Spring事务的嵌套的详细理解,以及事务失效的场景解惑
在TestService3中调用TestService4中的没有事务的方法,会发生什么呢?TestService4中的test41方法发生异常会回滚吗?

答案 会的
为什么呢请看下图:
Spring事务的嵌套的详细理解,以及事务失效的场景解惑
4.2 我们再来看一个情况
Spring事务的嵌套的详细理解,以及事务失效的场景解惑
TestService3中的方法是有事务的,TestService4中的方法是以非事务的方式运行,如果存在事务就挂起事务,那么自然就没有事物了
所以上述的情况就是,如果TestService4中的方法报出异常,则TestService3中的数据可以回滚,但是TestService4中操作的数据是不会回滚的

再有其他的情况大家根据上述的原理,然后结合传播属性的特点去套就可以了

总结4中的案例得到的结论是,如果是不同的类的事务嵌套的话,外层的方法按照外层的事务传播属性执行,内层的传播属性按照内层的传播属性的特点去运行

5、下边介绍一些目前我知道的Spring事务会失效的情况
5.1 事务方法访问修饰符不是public,导致事务失效
比如 下图
中的代码Spring事务的嵌套的详细理解,以及事务失效的场景解惑
解释,其实如果是JDK的动态代理 就不允许这种情况,因为JDK动态代理需要有接口,而接口中是不能有 私有方法的,如果是CGLIB动态代理的话也是不允许的代理private方法的

5.2 如果事务方法是static、final的,同样无法通过动态代理,事务也是不会生效的。Spring的声明式事务是基于动态代理实现的,我们无法重写final修饰的方法;不管是JDK动态代理还是Cglib的动态代理,就是要通过代理的方式获取到代理的具体对象,而static方法修饰的方法是属于类对象的,不属于任何实例对象,所以static方法不能被重写也就是说static方法也不被进行动态代理。

5.3 如下图中所示 @Transactional注解的方法抛出的异常不是注解中配置的spring的事务支持的异常,导致事务失效
Spring事务的嵌套的详细理解,以及事务失效的场景解惑
5.4 操作的数据库表如果本身不支持事务 那么配置的Spring事务 也会失效

5.6 代码中的异常被 catch 住,而且没有异常再次抛出,也会让spring的事务失效,如下图中所示 事务就会失效Spring事务的嵌套的详细理解,以及事务失效的场景解惑
想要 避免上述的失效的话 可以 在catch 住后在catch块中再次 抛出Spring事务支持的异常,就可以了

5.7 多线程的调用导致事务的失效,例如 下边的案例 会导致 test31的事务失效
Spring事务的嵌套的详细理解,以及事务失效的场景解惑
解释:原因是因为 test31中操作数据库的链接,test41在线程中操作的时候获取的数据库链接不是同一个数据库连接导致的事务失效,因为同一个事务中的数据库连接必须是同一个才可以保证同时提交和回滚,否则的话如果连接不一样,对应的数据库事务都是不同的事务,自然不能保证同时回滚或者提交了。

(上述的案例代码都经过验证,大家放心使用,发现有问题请动一下可爱的手指留下言哦)

好了,写了这么多希望可以帮到各位,觉得不错的话 记得点个赞哦。

相关文章

暂无评论

暂无评论...