Propagation事务传播方式

文章目录

  • Propagation属性值
  • REQUIRED与REQUIRED_NEW使用场景
  • UnexpectedRollbackException场景分析
    • 解决方案

spring Transaction 中一个很重要的属性:Propagation,只要用来配置当前需要执行的方法,与当前是否有transaction之间的关系

Propagation属性值

@see org.springframework.transaction.annotation.Propagation

事务传播方式 说明
PROPAGATION_REQUIRED 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是默认的传播方式
PROPAGATION_SUPPORTS 如当前有transaction,则在transaction状态下执行;如果当前没有transaction,在无transaction状态下执行
PROPAGATION_MANDATORY 必须在有transaction状态下执行,如果当前没有transaction,则抛出异常IllegalTransactionStateException
PROPAGATION_REQUIRES_NEW 创建新的transaction并执行;如果当前已有transaction,则将当前transaction挂起
PROPAGATION_NOT_SUPPORTED 在无transaction状态下执行;如果当前已有transaction,则将当前transaction挂起
PROPAGATION_NEVER 在无transaction状态下执行;如果当前已有transaction,则抛出异常IllegalTransactionStateException
PROPAGATION_SUPPORTS 如当前有transaction,则在transaction状态下执行;如果当前没有transaction,在无transaction状态下执行
PROPAGATION_NESTED 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。

REQUIRED与REQUIRED_NEW使用场景

从上面属性配置中,最难以理解并且容易在transaction设计时出现问题的是REQUIRED和REQUIRED_NEW这两者的区别,当程序某些情况下抛出异常,对这两者了解不够的话,就很难发现而且解决问题
下面对3个场景进行分析
Propagation事务传播方式_第1张图片

Propagation事务传播方式_第2张图片
Propagation事务传播方式_第3张图片
备注
多层嵌套事务中,如果使用了默认的事务传播方式,当内层事务抛出异常,外层事务捕捉并正常执行完毕时,就会报出rollback-only异常。
场景二具体分析

Propagation事务传播方式_第4张图片
Propagation事务传播方式_第5张图片

UnexpectedRollbackException场景分析

某些特殊的场景中,比如下面的业务层serviceA具有方法methodA,methodA会调用另一个业务层serviceB的方法methodB。在methodA的实际执行过程中,假如methodB有可能抛出异常,但在methodB无论执行是否正常都不能影响methodA的执行的情况下,通常需要methodA主动捕获这个异常。在methodA主动捕获这个异常的情况下,只要methodA的其他代码不抛出异常,则methodA是不会抛出任何异常的。既然methodA不会抛出异常,道理上讲作用于methodA上的事务是应该可以正常提交的呀,对不对?然而事实上,一旦methodB抛出异常,不管methodA的其他代码是否正确执行,整个事务是无法提交的(说这句话的前提是methodA和methodB上都具有事务,并且都由spring进行统一的事务管理)。

Propagation事务传播方式_第6张图片
出现上述异常是因为自己之前没有很好的理解spring事务的机制。 上述的methodA被调用执行时,有两个点是被spring事务代理的。也即serviceA.methodA()和serviceB.methodB(),这两个方法中只要有异常事件将回滚。 上述场景中存在事务嵌套,如果methodA中有异常出现事务会直接回滚,但methodB中有异常只是标记状态为需要回滚,最终在methodA中回滚。 上述场景中methodB有异常事务被标记为回滚,可是被methodA捕获了,也就不回滚了,一直执行到最后commit。在commit时spring会判断回滚标志,若检测到存在回滚标记, 则回滚事务并抛出UnexpectedRollbackException异常。

解决方案

  • 如果希望内层事务抛出异常时中断程序执行,直接在外层事务的catch代码块中抛出e.
  • 如果希望程序正常执行完毕,并且希望外层事务结束时全部提交,需要在内层事务中做异常捕获处理
  • 如果希望内层事务回滚,但不影响外层事务提交,需要将内层事务的传播方式指定为PROPAGATION_NESTED。注:PROPAGATION_NESTED基于数据库savepoint实现的嵌套事务,外层事务的提交和回滚能够控制嵌内层事务,而内层事务报错时,可以返回原始savepoint,外层事务可以继续提交。

你可能感兴趣的:(spring事务)