前言
Spring在TransactionDefinition接口中规定了7种类型的事务传播行为。事务传播行为是Spring框架独有的事务增强特性,他不属于的事务实际提供方数据库行为。这是Spring为我们提供的强大的工具箱,使用事务传播行可以为我们的开发工作提供许多便利。但是人们对他的误解也颇多,你一定也听过“service方法事务最好不要嵌套”的传言。要想正确的使用工具首先需要了解工具。本文对七种事务传播行为做详细介绍,内容主要代码示例的方式呈现。
基础概念
1. 什么是事务传播行为?
事务传播行为用来描述由某一个事务传播行为修饰的方法被嵌套进另一个方法的时事务如何传播。
用伪代码说明:
![](https://i-blog.csdnimg.cn/blog_migrate/cfe401cb059fae527ffe4632e5dca1c3.jpeg)
.
代码中methodA()方法嵌套调用了methodB()方法,methodB()的事务传播行为由@Transaction(Propagation=XXX)设置决定。这里需要注意的是methodA()并没有开启事务,某一个事务传播行为修饰的方法并不是必须要在开启事务的外围方法中调用。
2. Spring中七种事务传播行为
![](https://i-blog.csdnimg.cn/blog_migrate/18be437641f97a115e9ef561f7e9ae17.jpeg)
.
定义非常简单,也很好理解,下面我们就进入代码测试部分,验证我们的理解是否正确。
代码验证
文中代码以传统三层结构中两层呈现,即Service和Dao层,由Spring负责依赖注入和注解式事务管理,DAO层由Mybatis实现,你也可以使用任何喜欢的方式,例如,Hibernate,JPA,JDBCTemplate等。数据库使用的是MySQL数据库,你也可以使用任何支持事务的数据库,并不会影响验证结果。
首先我们在数据库中创建两张表:
user1
![](https://i-blog.csdnimg.cn/blog_migrate/c87fe40db17e82551d9452b4eabbee1d.jpeg)
.
user2
![](https://i-blog.csdnimg.cn/blog_migrate/82f78da36c5c97f04521f394f8aa72cb.jpeg)
.
然后编写相应的Bean和DAO层代码:
User1
![](https://i-blog.csdnimg.cn/blog_migrate/848ec04890021be1008baf5dfbcc590a.jpeg)
.
User2
![](https://i-blog.csdnimg.cn/blog_migrate/6bf03f666e1569b011b239d9b0bcf9c1.jpeg)
.
User1Mapper
![](https://i-blog.csdnimg.cn/blog_migrate/0050ad302a2f6596ab6beaca20f0cc92.jpeg)
.
User2Mapper
![](https://i-blog.csdnimg.cn/blog_migrate/0fe8e57cbacccbf7ddd12076cd60c6fb.jpeg)
.
最后也是具体验证的代码由service层实现,下面我们分情况列举。
1.PROPAGATION_REQUIRED
我们为User1Service和User2Service相应方法加上Propagation.REQUIRED属性。
User1Service方法:
![](https://i-blog.csdnimg.cn/blog_migrate/6b964ce33f387aa892c21572f118e0e9.jpeg)
.
User2Service方法:
![](https://i-blog.csdnimg.cn/blog_migrate/dca0b4e01301b4ee79f6d400bc1793cf.jpeg)
.
1.1 场景一
此场景外围方法没有开启事务。
验证方法1:
![](https://i-blog.csdnimg.cn/blog_migrate/9af193f57c1b1496a1d56574eea11b59.jpeg)
.
验证方法2:
![](https://i-blog.csdnimg.cn/blog_migrate/71c5e871ae13b0abd35e43baa472ac4c.jpeg)
.
分别执行验证方法,结果:
![](https://i-blog.csdnimg.cn/blog_migrate/b771635f7b98857c56a682c01fed71cb.jpeg)
.
结论:通过这两个方法我们证明了在外围方法未开启事务的情况下Propagation.REQUIRED修饰的内部方法会新开启自己的事务,且开启的事务相互独立,互不干扰。
1.2 场景二
外围方法开启事务,这个是使用率比较高的场景。
验证方法1:
![](https://i-blog.csdnimg.cn/blog_migrate/a57934af7c075dba291756828baf9055.jpeg)
.
验证方法2:
![](https://i-blog.csdnimg.cn/blog_migrate/e901d55c45f5ba552864292198bf5f3b.jpeg)
.
验证方法3:
![](https://i-blog.csdnimg.cn/blog_migrate/3c7b68b57864e04fc2aca59ccd243956.jpeg)
.
分别执行验证方法,结果:
![](https://i-blog.csdnimg.cn/blog_migrate/b3fdddaea379596bfc5a4658645f239d.jpeg)
.
结论:以上试验结果我们证明在外围方法开启事务的情况下Propagation.REQUIRED修饰的内部方法会加入到外围方法的事务中,所有Propagation.REQUIRED修饰的内部方法和外围方法均属于同一事务,只要一个方法回滚,整个事务均回滚。
2.PROPAGATION_REQUIRES_NEW
我们为User1Service和User2Service相应方法加上Propagation.REQUIRES_NEW属性。
User1Service方法:
![](https://i-blog.csdnimg.cn/blog_migrate/64ba18ac2259bfd1981981189e4fdbd2.jpeg)
.
User2Service方法:
![](https://i-blog.csdnimg.cn/blog_migrate/85b92086b4e1f766d7a71e52273cc596.jpeg)
.
2.1 场景一
外围方法没有开启事务。
验证方法1:
![](https://i-blog.csdnimg.cn/blog_migrate/7ecb994a7585c28712689c512a1acc89.jpeg)
.
验证方法2:
![](https://i-blog.csdnimg.cn/blog_migrate/3ab71e5d2d0174a7b82edf0c5f62cbf5.jpeg)
.
分别执行验证方法,结果:
![](https://i-blog.csdnimg.cn/blog_migrate/ec119e7fa7dbbd09ae998fd19d8844ef.jpeg)
.
结论:通过这两个方法我们证明了在外围方法未开启事务的情况下Propagation.REQUIRES_NEW修饰的内部方法会新开启自己的事务,且开启的事务相互独立,互不干扰。
2.2 场景二
外围方法开启事务。
验证方法1:
![](https://i-blog.csdnimg.cn/blog_migrate/c0838abd09f9f8b65942e6433dc05c22.jpeg)
.
验证方法2:
![](https://i-blog.csdnimg.cn/blog_migrate/e1adff3cfd0a8fc1254b2052642a6aa3.jpeg)
.
验证方法3:
![](https://i-blog.csdnimg.cn/blog_migrate/f01bb7bdadd881ad851418b1ce9a5a1f.jpeg)
.
分别执行验证方法,结果:
![](https://i-blog.csdnimg.cn/blog_migrate/7725324fa90163bb160f01802156885e.jpeg)
.
结论:在外围方法开启事务的情况下Propagation.REQUIRES_NEW修饰的内部方法依然会单独开启独立事务,且与外部方法事务也独立,内部方法之间、内部方法和外部方法事务均相互独立,互不干扰。
3.PROPAGATION_NESTED
3.1 场景一
此场景外围方法没有开启事务。
验证方法1:
![](https://i-blog.csdnimg.cn/blog_migrate/0ae1e81b6a22f044f2da824df2bb3654.jpeg)
.
验证方法2:
![](https://i-blog.csdnimg.cn/blog_migrate/3174ef7326b15f5f62a9cb21a871c71f.jpeg)
.
分别执行验证方法,结果:
![](https://i-blog.csdnimg.cn/blog_migrate/ef97262bdee3574c11ca8e65e30ef6cb.jpeg)
.
结论:通过这两个方法我们证明了在外围方法未开启事务的情况下Propagation.NESTED和Propagation.REQUIRED作用相同,修饰的内部方法都会新开启自己的事务,且开启的事务相互独立,互不干扰。
3.2 场景二
外围方法开启事务。
验证方法1:
![](https://i-blog.csdnimg.cn/blog_migrate/4ab0bdfb1ade99b5b90a79b660a9329b.jpeg)
.
验证方法2:
![](https://i-blog.csdnimg.cn/blog_migrate/ddb66dffe58a695e82182618d02593a6.jpeg)
.
验证方法3:
![](https://i-blog.csdnimg.cn/blog_migrate/9b9c9e08dcfa986ff660e4017c7e059f.jpeg)
.
分别执行验证方法,结果:
![](https://i-blog.csdnimg.cn/blog_migrate/7761134893bcae92e04beca581edbaa1.jpeg)
.
结论:以上试验结果我们证明在外围方法开启事务的情况下
Propagation.NESTED
修饰的内部方法属于外部事务的子事务,外围主事务回滚,子事务一定回滚,而内部子事务可以单独回滚而不影响外围主事务和其他子事务
4. REQUIRED,REQUIRES_NEW,NESTED异同
由“1.2 场景二”和“3.2 场景二”对比,我们可知:
NESTED和REQUIRED修饰的内部方法都属于外围方法事务,如果外围方法抛出异常,这两种方法的事务都会被回滚。但是REQUIRED是加入外围方法事务,所以和外围事务同属于一个事务,一旦REQUIRED事务抛出异常被回滚,外围方法事务也将被回滚。而NESTED是外围方法的子事务,有单独的保存点,所以NESTED方法抛出异常被回滚,不会影响到外围方法的事务。
由“2.2 场景二”和“3.2 场景二”对比,我们可知:
NESTED和REQUIRES_NEW都可以做到内部方法事务回滚而不影响外围方法事务。但是因为NESTED是嵌套事务,所以外围方法回滚之后,作为外围方法事务的子事务也会被回滚。而REQUIRES_NEW是通过开启新的事务实现的,内部事务和外围事务是两个事务,外围事务回滚不会影响内部事务。
5. 其他事务传播行为
鉴于文章篇幅问题,其他事务传播行为的测试就不在此一一描述了,感兴趣的读者可以去源码中自己寻找相应测试代码和结果解释。
结论
通过上面的介绍,相信大家对Spring事务传播行为有了更加深入的理解,希望大家有所帮助。