18个示例详解 Spring 事务传播机制

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了18个示例详解 Spring 事务传播机制相关的知识,希望对你有一定的参考价值。

参考技术A

事务的传播机制,顾名思义就是多个事务方法之间调用,事务如何在这些方法之间传播。

Spring 对事务的传播机制在 Propagation 枚举中定义了7个分类:

事务的传播机制,是 spring 规定的。因为在开发中,最简单的事务是,业务代码都处于同一个事务下,这也是默认的传播机制,如果出现的报错,所有的数据回滚。 但是在处理复杂的业务逻辑时,方法之间的调用,有以下的需求:

首先创建两个方法 A 和 B 实现数据的插入,插入数据A:

插入数据B:

使用伪代码创建 mainTest 方法和 childTest 方法

main 调用 test 方法,其中

以上伪代码,调用 mainTest 方法,如果mainTest 和childTest 都不使用事务 的话,数据存储的结果是如何呢?

因为都没使用事务,所以 a1 和 b1 都存到成功了,而之后抛出异常之后,b2是不会执行的。所以 a1 和 b1 都插入的数据,而 b2 没有插入数据。

如果当前不存在事务,就新建一个事务。如果存在事务,就加入到当前事务。这是一个默认的事务

示例1 :根据场景举个例子,在 childTest 添加事务,设置传播属性为 REQUIRED,伪代码如下:

因为 mainTest 没有事务,而 childTest 又是新建一个事务,所以 a1 添加成功。在 childTest 因为抛出了异常,不会执行 b2 添加,而 b1 添加回滚。最终 a1 添加成功,b1没添加成功。

示例2 :在 mainTest 和 childTest 都添加事务,传播属性都为 REQUIRED,伪代码如下:

根据 REQUIRED 传播属性,如果存在事务,就加入到当前事务。两个方法都属于同一个事务,同一个事务的话,如果有发生异常,则全部都回滚。所以 a1 和 b1 都没添加成功。

如果当前没有事务,则以非事务的方式运行。如果存在事务,就加入到当前事务。

示例3 :childTest 添加事务,传播属性设置为 SUPPORTS,伪代码如下:

传播属性为 SUPPORTS,如果没有事务,就以非事务的方式运行。表明两个方法都没有使用事务,没有事务的话,a1、b1 都添加成功。

示例4 :mainTest 添加事务,设置传播属性为 REQUIRED。childTest 添加事务,设置传播属性为 SUPPORTS,伪代码如下:

SUPPORTS 传播属性,如果存在事务,就加入到当前事务。mainTest 和 childTest 都属于同一个事务,而 childTest 抛出异常,a1 和b1 添加都回滚,最终 a1、b1 添加失败。

如果存在事务,就加入到当前事务。如果不存在事务,就报错 。 这就说明如果想调用 MANDATORY 传播属性的方法,一定要有事务,不然就会报错。

示例5 : 首先在 childTest 添加事务,设置传播属性为 MANDATORY,伪代码如下:

在控制台直接报错:

说明被标记为 mandatory 传播属性没找到事务,直接报错。因为 mainTest 没有事务,a1 添加成功。而 childTest 由于报错,b1 添加失败。

示例6 : mainTest 添加事务,设置传播属性为 REQUIRED。childTest 添加事务,设置传播属性为 MANDATOR,伪代码如下:

如果存在事务,就把事务加入到当前事务。同一个事务中 childTest 抛出异常,a1 和 b1 添加被回滚,所以a1 和 b1添加失败。

创建一个新的事务。如果存在事务,就将事务挂起。

示例7 :childTest 添加事务,设置传播属性为 REQUIRES_NEW,伪代码如下:

mainTest 不存在事务,a1 添加成功,childTest 新建了一个事务,报错,回滚 b1。所以 a1 添加成功,b1 添加失败。

示例8 :mainTest 添加事务,设置传播属性为 REQUIRED。childTest 添加事务,设置传播属性为 REQUIRES_NEW,伪代码如下:

mainTest 创建了一个事务,childTest 新建一个事务,在 childTest 事务中,抛出异常,b1 回滚,异常抛到 mainTest 方法,a1 也回滚,最终 a1 和 b1 都回滚。

示例9 :在 示例8 中,如果不想让 REQUIRES_NEW 传播属性影响到被调用事务,将异常捕获就不会影响到被调用事务。

childTest 抛出了异常,在 mainTest 捕获了,对 mainTest 没有影响,所以 b1 被回滚,b1 添加失败,a1 添加成功。

示例10 :mainTest 设置传播属性为 REQUIRED,并在 mainTest 抛出异常。childTest 同样设置 REQUIRES_NEW 传播属性,伪代码如下:

childTest 是一个新建的事务,只要不抛出异常是不会回滚,所以 b1 添加成功,而 mainTest 抛出了异常,a1 添加失败。

无论是否存在当前事务,都是以非事务的方式运行

示例11 :childTest 添加事务,设置传播属性为 NOT_SUPPORTED,伪代码如下:

NOT_SUPPORTED 都是以非事务的方式执行,childTest 不存在事务,b1 添加成功。而 mainTest 也是没有事务,a1 也添加成功。

示例12 :childTest 添加事务,设置传播属性为 NOT_SUPPORTED,mainTest 添加默认传播属性 REQUIRED,伪代码如下:

其中 childTest 都是以非事务的方式执行,b1 添加成功。而 mainTest 存在事务,报错后回滚,a1 添加失败。

不使用事务,如果存在事务,就抛出异常

示例13 :childTest 添加 NEVER 传播属性,伪代码如下:

NEVER 不使用事务,mainTest 也不使用事务,所以 a1 和 b1 都添加成功,b2添加失败。

示例14 : mainTest 添加 REQUIRED 传播属性,childTest 传播属性设置为 NEVER,伪代码如下:

mainTest 存在事务,导致 childTest 报错,b1添加失败,childTest 抛错到 mainTest,a1 添加失败。

如果当前事务存在,就运行一个嵌套事务。如果不存在事务,就和 REQUIRED 一样新建一个事务。

示例15 : childTest 设置 NESTED 传播属性,伪代码如下:

在 childTest 设置 NESTED 传播属性,相当于新建一个事务,所以 b1 添加失败, mainTest 没有事务,a1 添加成功。

示例16 :设置 mainTest 传播属性为 REQUIRED,新建一个事务,并在方法最后抛出异常。 childTest 设置属性为 NESTED,伪代码如下:

childTest 是一个嵌套的事务,当主事务的抛出异常时,嵌套事务也受影响,即 a1、b1 和 b2 都添加失败。和 示例10 不同的是, 示例10 不会影响 childTest 事务。

示例17 :和 示例16 一样,在 mainTest 设置传播属性为 REQUIRED,childTest 设置传播属性为 NESTED,不同的是,在 mainTest 捕获 childTest 抛出的异常,伪代码如下:

childTest 是一个子事务,报错回滚,b1 和 b2 都添加失败。而 mainTest 捕获了异常,不受异常影响,a1 添加成功。

示例18 :将 示例2 改造一下,mainTest 捕获 childTest 抛出的异常,伪代码如下:

mainTest 和 childTest 两个方法都处于同一个事务,如果有一个方法报错回滚,并且被捕获。整个事务如果还有数据添加就会抛出 Transaction rolled back because it has been marked as rollback-only 异常,同一个事务,不能出现有的回滚了,有的不回滚,要么一起回滚,要不一起执行成功。所以全部数据都添加失败。

Spring的七种事务传播机制

概述

当我们调用一个基于Spring的Service接口方法(如UserService#addUser())时,它将运行于Spring管理的事务环境中,Service接口方法可能会在内部调用其它的Service接口方法以共同完成一个完整的业务操作,因此就会产生服务接口方法嵌套调用的情况, Spring通过事务传播行为控制当前的事务如何传播到被嵌套调用的目标服务接口方法中。

事务传播是Spring进行事务管理的重要概念,其重要性怎么强调都不为过。但是事务传播行为也是被误解最多的地方,在本文里,我们将详细分析不同事务传播行为的表现形式,掌握它们之间的区别。

事务传播行为种类

Spring在TransactionDefinition接口中规定了7种类型的事务传播行为,它们规定了事务方法事务方法发生嵌套调用时事务如何进行传播:

1事务传播行为类型

事务传播行为类型

说明

PROPAGATION_REQUIRED

如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。

PROPAGATION_SUPPORTS

支持当前事务,如果当前没有事务,就以非事务方式执行。

PROPAGATION_MANDATORY

使用当前的事务,如果当前没有事务,就抛出异常。

PROPAGATION_REQUIRES_NEW

新建事务,如果当前存在事务,把当前事务挂起。

PROPAGATION_NOT_SUPPORTED

以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。

PROPAGATION_NEVER

以非事务方式执行,如果当前存在事务,则抛出异常。

PROPAGATION_NESTED

如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。

PROPAGATION_REQUIRES_NEW 启动一个新的, 不依赖于环境的 "内部" 事务. 这个事务将被完全 commited 或 rolled back 而不依赖于外部事务, 它拥有自己的隔离范围, 自己的锁, 等等. 当内部事务开始执行时, 外部事务将被挂起, 内务事务结束时, 外部事务将继续执行.

PROPAGATION_NOT_SUPPORTED 外部方法的Transaction暂停直至innerMethod执行完毕

 

以上是关于18个示例详解 Spring 事务传播机制的主要内容,如果未能解决你的问题,请参考以下文章

Spring事务传播机制详解

Spring事务传播行为详解(示例版)

Spring中的事务传播属性详解

Spring声明式事务@Transactional 详解,事务隔离级别和传播行为

事务的传播机制

事务的传播机制