关于Spring 事务管理传播属性的配置及作用-嵌套事务

时间:2022-08-02 05:18:52

先了解事务的7种传播属性:

PROPAGATION_REQUIRED -- 支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
PROPAGATION_SUPPORTS -- 支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY -- 支持当前事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW -- 新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED -- 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER -- 以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED -- 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。
前六个策略类似于EJB CMT,第七个(PROPAGATION_NESTED)是Spring所提供的一个特殊变量。
它要求事务管理器或者使用JDBC 3.0 Savepoint API提供嵌套事务行为(如Spring的DataSourceTransactionManager)

问题产生场景:

 1.在 DemoServiceA.java中有方法 demoMethodA().其中嵌套DemoServiceB.java中demoMethodB()。当demoMethodA事务发生rollback时, demoMethodB 事务也可以rollback或是commit。

如图:

public class DemoServiceA {

    public void demoMethodA() {

        demoServiceB.demoMethodB();//Insert对象B 操作
} }

常见解决方案: 

public class DemoServiceA {

    /**
* 新建事务
* 事务属性配置为 PROPAGATION_REQUIRED
*/
@Transactional(propagation=Propagation.REQUIRED)
public void demoMethodA() {
//操作... /**
* 1.事务属性配置为 PROPAGATION_REQUIRES_NEW ;
* A. DemoServiceA 事务commit与rollback,与 DemoServiceB无任何关系,DemoServiceB 不属于事务 DemoServiceA的子事务。
* PROPAGATION_REQUIRES_NEW 启动一个新的, 不依赖于环境的 "内部" 事务. 这个事务将被完全 commited
* 或 rolled back 而不依赖于外部事务, 它拥有自己的隔离范围, 自己的锁, 当内部事务开始执行时,
* 外部事务将被挂起, 内务事务结束时, 外部事务将继续执行.
* B. 可以起到分支执行的效果。service方法虽然嵌套但是事务之间状态相互无影响
*
*
*
* 2.事务属性配置为 PROPAGATION_NESTED;
* PROPAGATION_NESTED 开始一个 "嵌套的" 事务, 它是已经存在事务的一个真正的子事务.
* 潜套事务开始执行时, 它将取得一个 savepoint. 如果这个嵌套事务失败,
* 我们将回滚到此 savepoint. 潜套事务是外部事务的一部分, 只有外部事务结束后它才会被提交.
*
*/
demoServiceB.demoMethodB();//Insert对象B 操作
//操作... }
}

下面详细介绍7种事务传播属性在示例中的作用:

: REQUIRED

加入当前正要执行的事务不在另外一个事务里,那么就起一个新的事务

比如说,DemoServiceB.demoMethodB的事务级别定义为REQUIRED, 那么由于执行DemoServiceA.demoMethodA的时候,

DemoServiceA.demoMethodA已经起了事务,这时调用DemoServiceB.demoMethodB,DemoServiceB.demoMethodB看到自己已经运行在DemoServiceA.demoMethodA

的事务内部,就不再起新的事务。而假如DemoServiceA.demoMethodA运行的时候发现自己没有在事务中,他就会为自己分配一个事务。

这样,在DemoServiceA.demoMethodA或者在DemoServiceB.demoMethodB内的任何地方出现异常,事务都会被回滚。即使DemoServiceB.demoMethodB的事务已经被

提交,但是DemoServiceA.demoMethodA在接下来fail要回滚,DemoServiceB.demoMethodB也要回滚

: SUPPORTS

如果当前在事务中,即以事务的形式运行,如果当前不再一个事务中,那么就以非事务的形式运行

: MANDATORY

必须在一个事务中运行。也就是说,他只能被一个父事务调用。否则,他就要抛出异常

: REQUIRES_NEW

这个就比较绕口了。 比如我们设计DemoServiceA.demoMethodA的事务级别为REQUIRED,DemoServiceB.demoMethodB的事务级别为REQUIRES_NEW,

那么当执行到DemoServiceB.demoMethodB的时候,DemoServiceA.demoMethodA所在的事务就会挂起,DemoServiceB.demoMethodB会起一个新的事务,等待DemoServiceB.demoMethodB的事务完成以后,

他才继续执行。他与REQUIRED 的事务区别在于事务的回滚程度了。因为DemoServiceB.demoMethodB是新起一个事务,那么就是存在

两个不同的事务。如果DemoServiceB.demoMethodB已经提交,那么DemoServiceA.demoMethodA失败回滚,DemoServiceB.demoMethodB是不会回滚的。如果DemoServiceB.demoMethodB失败回滚,

如果他抛出的异常被DemoServiceA.demoMethodA捕获,DemoServiceA.demoMethodA事务仍然可能提交。

: NOT_SUPPORTED

当前不支持事务。比如DemoServiceA.demoMethodA的事务级别是REQUIRED ,而DemoServiceB.demoMethodB的事务级别是NOT_SUPPORTED ,

那么当执行到DemoServiceB.demoMethodB时,DemoServiceA.demoMethodA的事务挂起,而他以非事务的状态运行完,再继续DemoServiceA.demoMethodA的事务。

: NEVER

不能在事务中运行。假设DemoServiceA.demoMethodA的事务级别是REQUIRED, 而DemoServiceB.demoMethodB的事务级别是NEVER ,

那么DemoServiceB.demoMethodB就要抛出异常了。

: NESTED

理解Nested的关键是savepoint。他与REQUIRES_NEW的区别是,REQUIRES_NEW另起一个事务,将会与他的父事务相互独立,

而Nested的事务和他的父事务是相依的,他的提交是要等和他的父事务一块提交的。也就是说,如果父事务最后回滚,他也要回滚的。