Spring嵌套事务机制
之前写过一篇文章,是关于Spring嵌套事务机制的,回过头来看发现有不少问题,但暂时不想删掉它,所以再写一篇文章更正一下之前的错误。这里先再次说明一下PROPAGATION_NESTED的作用,若当前存在事务则以嵌套事务方式执行,若不存在事务则创建一个新事务。
考虑到动态代理的实现方式,同一个类内部不同方法之间的调用只有外部方法的代理会生效,因此这里的讨论不考虑同一个类内部方法之间互相调用的情况,只考虑不同的类之间的方法进行调用的情况。
我们知道嵌套事务底层实际是利用SAVEPOINT实现的,因此外部事务回滚必然会导致整个事务回滚。若外部没有事务,则内部事务是否回滚取决于内部代码。这里假设调用方法叫ServiceA,被调用方法叫ServiceB。A上面标注的是PROPAGATION_REQUIRED,B上面标注的是PROPAGATION_NESTED。我们假设现在代码发生了异常,异常发生的位置存在三种情况:①发生在A处、②发生在B处、③两处都发生了异常。
若是情况①,内部事务包含在外部事务中,因此内外部的事务都会回滚。
若是情况②,还得再细分为两种情况,即A里面是否忽略B的异常,若A忽略B产生的异常,则内部事务回滚,外部事务正常提交。若A将B的异常继续往上抛出,则内外部事务都会回滚。
若是情况③,那自然不用多解释,内外部事务必然都会回滚。
可能出现的情况大概就这么几种,使用起来其实也不复杂,知道底层的原理就行啦。
再补充几点使用Spring事务的细节吧。之前做过一些实验,正好一起记录一下。主要有三点,使用声明式事务的注意事项,内部调用的事务生效情况,跨类调用的事务生效情况。
使用声明式事务时,无论是JDK动态代理,还是CGLIB动态代理,都只会为public方法生成事务,因此给非public方法加@Transactional注解是不生效的。如果你要问为啥这样实现,理由就是Spring就是这么实现的,个人理解就是为了避免引入不必要的复杂度,因为理论上动态代理也是可以代理protected方法的。
调用代理类中的非事务方法时,即使该方法内部调用了代理类中其他的事务方法,内部方法的事务也是不会生效的。因为Spring同时维护了目标对象和代理对象,不需要增强的方法Spring会直接走目标对象,所以这种方式下事务就不生效了。
跨类调用时,若外部方法未开启事务则外部方法中的数据不会回滚,内部方法则由该方法的事务配置决定。若外部方法开启事务,默认配置下内外部方法都会存在事务,除非自定义了事务传播属性。
Spring事务相关的重要的知识点差不多就这些了。