1.spring事务理解
a.想起当初在测试spring的声明式事务时,有很多问题没想明白,尤其是事务传递的特性。
在controller层调用两个service层方法,每个方法都加上@Transactional(REQUIRES),但最后的结果是两个方法相互独立地提交了事务,
第二个方法的事务并没有加入到第一个方法事务中。根本就没有REQUIRES的特性。
直到现在,经常地产生各种service嵌套调用的情景(其实是不规范的,代码也难看),并且每个service类都继承了开启事务的父类,
即每个方法都开启事务(这个做法更加值得诟病),才明白spring的事务传播机制是怎样工作的。
b.这里说一下基本概念,spring的事务传播是基于事务嵌套的基础上,这里有三个嵌套的service方法:
class A{ B b = new B; fun a(){b.b();} }
class B{ C c = new C; fun b(){c();c.d()} , fun c()}
class C{ fun d()}
现在每个方法都加上注解@Transactional(REQUIRES)。
(1)首先要说的spring开启事务的机制。
如果是通过注解标注事务的方法,并不是进入这个方法就会开启事务。事实上,只有通过{对象.方法}的方式才会开启事务(外部调用)。
在上面的例子里面:
a被调用时,开启了一个事务。
a调用b,开启一个事务。
b调用c,在b的事务中。
b调用d,开启一个事务。
所以一共有三个事务。
(2)事务的父子关系。
在嵌套的事务中,a是b的父事务,b是a的子事务;b和d的关系亦然。
(3)事务的传播机制。
由于每一个方法都标注了@Transactional(REQUIRES),即需要事务,传播机制规定父事务必需在子事务正确提交的情况下才可以提交。
现在假设d方法是一个日志记录(非必需)方法,我希望无论这个方法是否有错,都不影响其他业务的运行,
所以在b方法内对其进行异常捕捉并不做任何处理。
如果所有方法都没有出错,事务固然可以一步步执行提交,但如果d方法出错,那么在a方法会抛出异常:
Transaction rolled back because it has been marked as rollback-only
意思是事务已经标记为rollback-only,那么事务需要回滚,不能提交。
rollback-only是一个全局性的东西,因为子事务d已经失败回滚了,rollback-only会set true,父事务就不能正常提交了。
spring事务的传播机制就体现在这里了,虽然每个方法都各自执行事务,但仍然是联系在一起的。
(4)以上的问题应该如何解决?
(a)简单的方法,d不要加事务。
(b)如果真的要加事务,可以用@Transactional(REQUIRES_NEW)
(5)其实,个人认为事务不应该这样用,除非业务逻辑真的有这样的必要性。这样嵌套的代码打破了j4的简洁性,使逻辑变得复杂且难于理解。
而事务的使用本身也应该谨慎,开发者应该明白自己的每一个逻辑为什么需要加事务以及怎样回滚。全局开启事务是一种很糟糕的做法,
一方面导致以上的尴尬(你永远不知道这些嵌套的事务什么时候导致不可预料的逻辑错误),另一方面造成毫无必要的性能损耗。
2018.12.22