事务传播行为
“事务传播行为”描述的是:当一个事务方法被另一个方法调用时,该事务方法如何进行?
是创建新事务?丢弃事务?还是加入到已存在的事务呢?
针对这些情况,Spring框架定义了七种事务传播行为,开发人员可以根据实际的业务场景来选择合适的传播行为。
七种事务传播行为
Spring将其定义在一个枚举类Propagation
中,分别如下:
- REQUIRED
如果当前没有开启事务,就开启一个新事务,如果当前开启了事务,就加入该事务,默认行为。 - SUPPORTS
如果当前开启了事务,就加入该事务,否则非事务执行。 - MANDATORY
如果当前开启了事务,就加入该事务,否则抛出异常。说白了,强制事务,不允许非事务执行。 - REQUIRES_NEW
始终创建新事务,如果当前开启了事务,则将当前事务挂起。 - NOT_SUPPORTED
强制以非事务执行,如果当前开启了事务就将当前事务挂起再执行。 - NEVER
非事务执行,如果当前开启了事务则抛出异常。 - NESTED
如果当前开启了事务,则在嵌套事务内执行,否则开启一个事务执行。
特别提醒
Spring的事务是基于代理类通过AOP来实现的,如果希望事务传播生效,那么必须通过Spring生成的代理类来调用方法,Spring在增强代理类中判断是否要开启事务,在同一个类中直接调用自身方法Spring是无法帮我们开启事务的!
如下例子,事务传播行为不会生效:
调用不同的Service,因为调用的代理类,所以事务传播会生效:
七种传播行为示例
REQUIRED
Spring默认的事务传播行为,也是用的最多的。
如果当前没有开启事务,就开启一个新事务,如果当前开启了事务,就加入该事务,默认行为。
如上图,save没有开启事务,insert不会回滚,delete开启了事务,delete会回滚。
如果save开启了事务,则delete不会开启新事务,会加入到save的事务中,insert和delete都会回滚。
SUPPORTS
如果当前开启了事务,就加入该事务,否则非事务执行。
如上图,save没有开启事务,delete也不会开启事务,insert和delete操作均不会回滚。
如果save开启了事务,delete会加入到save的事务中,则insert和delete均会回滚。
MANDATORY
如果当前开启了事务,就加入该事务,否则抛出异常。说白了,强制事务,不允许非事务执行。
如上图,save没有开启事务,执行delete()时会抛出异常:
insert成功,delete操作没有执行。
如果save开启了事务,则delete()加入到事务中,不会抛异常。
REQUIRES_NEW
如果当前没有开启事务,则开启一个事务,事务的执行。
如果当前开启了事务,则将当前事务挂起,开启一个新事务,事务的执行。
外层事务和内层事务是互相隔离的,内层事务的提交或回滚不影响外层事务,外层事务的提交或回滚也不影响内层事务。
需要捕获内层方法异常,否则内层方法抛异常,会影响外层事务回滚。
如上图,save开启了事务A,执行delete()时,会将save的事务A挂起,并开启新事务B,delete()执行成功,事务B提交,然后恢复事务A,接着抛出异常,事务A回滚,但是事务B已经提交不会受到影响。
最终delete操作成功,insert操作被回滚。
从控制台的日志也能看见其运行过程:
NOT_SUPPORTED
强制以非事务执行,如果当前开启了事务就将当前事务挂起再执行。
如上图,save开启了事务A,执行delete()时会挂起事务A,然后以非事务的方式执行delete并提交,然后恢复事务A,由于save抛出异常,insert回滚。
结果:insert操作回滚,delete操作成功。
NEVER
非事务执行,如果当前开启了事务则抛出异常。
如上图,由于save开启了事务,导致delete抛出异常:
如果save没有开启事务,则save和delete都会以非事务执行。
NESTED
如果当前没有开启事务,则开启一个新事务,事务的执行。
否则,在当前事务中以嵌套事务的方式执行。
以嵌套事务执行前,会创建一个保存点savePoint,如果方法异常,则回滚到该savePoint,否则等待和外层事务一起Commit。
关于Spring的七种事务传播行为都介绍完了,根据实际应用场景选择需要的事务传播行为即可。
REQUIRES_NEW和NESTED的区别
REQUIRES_NEW和NESTED这两种传播行为比较容易搞混,但其实它俩有本质区别。
在当前已经开启事务的情况下:
REQUIRES_NEW会开启一个和外层事务真正隔离的内层事务,内层方法执行完后,内层事务就已经提交或回滚了,和外层事务是完全隔离的,互不影响的。内层事务结束后,才会恢复外层事务,外层事务继续执行。
NESTED本质并没有开启一个新的事务,而是以创建savePoint的方式嵌套的执行,不管内层事务是否回滚或提交,最终都由外层事务来一并执行。