Spring中的事务传播行为示例详解

一、背景介绍

Spring 框架应该是每一个人 javaer 都必须接触和学习的技术,Spring 公司所提供的各种框架是 Java 开发行业可参考的重要标准之一。

Spring 中有 7 种类型的事务传播行为。事务传播行为是 Spring 框架提供的一种事务管理方式,它是 Spring 框架之中非常重要的一个技术点,毕竟事务关系到应用程序和数据库的交互,而数据更是互联网行业最为重要的资源。

平时开发过程中事务都会有使用,但是没有真正地总结过,尤其是事务中嵌套事务的场景,此篇基础知识文在此做一个简单的学习总结,

二、事务

1、事务的特征

  • 原子性:一个事务中所有对数据库的操作是一个不可分割的操作序列,要么全做要么全不做
  • 一致性:数据不会因为事务的执行而遭到破坏
  • 隔离性:一个事物的执行,不受其他事务的干扰,即并发执行的事物之间互不干扰
  • 持久性:一个事物一旦提交,它对数据库的改变就是永久的

2、事务的隔离级别

  1. read_uncommitted:读未提交,一个事务可以感知或者操作另外一个未提交的事务,可能会出现脏读、不可重复读、幻读
  2. read_committed:读已提交,一个事务只能感知或者操作另一个已经提交的事务,可能会出现不可重复读、幻读
  3. repeatable_read:可重复读,能够避免脏读,不可重复读,不能避免幻读
  4. serializable:串行化,隔离级别最高,消耗资源最低,代价最高,能够防止脏读, 不可重复读,幻读。

mysql 默认的事务隔离级别是 repeatable_read

3、Spring 事务的传播行为

  1. Propagation.REQUIRED(默认):如果当前存在事务,则加入该事务,如果当前不存在事务,则创建一个新的事务。
  2. Propagation.SUPPORTS:如果当前存在事务,则加入该事务;如果当前不存在事务,则以非事务的方式继续运行。
  3. Propagation.MANDATORY:如果当前存在事务,则加入该事务;如果当前不存在事务,则抛出异常。
  4. Propagation.REQUIRES_NEW:重新创建一个新的事务,如果当前存在事务,延缓当前的事务。
  5. Propagation.NOT_SUPPORTED:以非事务的方式运行,如果当前存在事务,暂停当前的事务。
  6. Propagation.NEVER:以非事务的方式运行,如果当前存在事务,则抛出异常。
  7. Propagation.NESTED:如果没有,就新建一个事务;如果有,就在当前事务中嵌套其他事务。

三、代码演示

准备动作:新建一个 spring 项目,数据库脚本见:

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for customer
-- ----------------------------
DROP TABLE IF EXISTS `customer`;
CREATE TABLE `customer` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `name` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL,
 `remark` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL,
 PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;

-- ----------------------------
-- Records of customer
-- ----------------------------
BEGIN;
INSERT INTO `customer` VALUES (1, 'A', 'a');
INSERT INTO `customer` VALUES (2, 'B', 'b');
INSERT INTO `customer` VALUES (3, 'C', 'c');
COMMIT;

SET FOREIGN_KEY_CHECKS = 1;

测试步骤为:方法 A 中调用方法 B 和 方法 C,三个方法各自操作数据库中的一条数据,然后方法 A、B、C 组合添加事务

伪代码为:

function A(){ // 方法 A
 functionB(); // 方法 B
 functionC(); // 方法 C
}

① Propagation.REQUIRED(默认)

步骤:

@Transactional
function A(){ // 有事务
 functionB(); // 无事务,代码正常
 functionC(); // 无事务,代码抛异常
}

结果:

数据库中无数据

说明:

方法 B 和方法 C 虽然没有自己的事务,但是外层的方法 A 是有事务的,由于默认的事务传播机制,所以方法 B 和 C 都加入到了 A 的事务之中,所以当 C 报错时,B 也随着回滚了。

② Propagation.SUPPORTS

步骤:

@Transactional
function A(){ // 有事务
 functionB(); // 无事务,代码正常
 functionC(); // 无事务,代码抛异常
}

结果:

数据库中有两条数据

说明:

Propagation.SUPPORTS 的意思是当前有事务则加入,没有则不添加事务,所以,由于 A 是没有事务的,那么 B 和 C 自然而然也就不加入事务中,而 C 中的报错了,那么自然而然也就不会进行数据回滚。

③ Propagation.MANDATORY

步骤:

@Transactional
function A(){ // 有事务
 functionB(); // 无事务,代码正常
 functionC(); // 无事务,代码抛异常
}

结果:

不仅数据库中无数据,而且运行时报错:

org.springframework.transaction.IllegalTransactionStateException: No existing transaction found for transaction marked with propagation 'mandatory'

说明:

Propagation.MANDATORY 的意思是当前有事务则加入,没有事务则抛出异常。所以上述代码在运行时抛出了 IllegalTransactionStateException 异常。

④ Propagation.REQUIRES_NEW

步骤:

function A(){ // 有事务
 insert one data;

 functionB(); // 有事务,代码正常,事务为:@Transactional(propagation = Propagation.REQUIRES_NEW)
 functionC(); // 有事务,代码抛异常,事务为:@Transactional(propagation = Propagation.REQUIRES_NEW)
}

结果:

数据库中仅有方法 B 的一条数据。

说明:

Propagation.REQUIRES_NEW 的意思是重新创建一个新的事务,如果当前存在事务,延缓当前的事务。

这个理解起来有点拗。结合上述代码案例来看,首先 B 和 C 都是有各自的事务的,所以在 C 抛出异常后,C 会回滚,而 B 则会正常插入一条数据,毕竟两者的事务的独立的,但是此时 A 也是有事务的,并且 A 的事务是在 B 和 C 之后再执行的,所以当 C 抛出异常之后,A 自然而然也就捕获到了,那么 A 的事务也会回滚,所以数据库中 A 插入的数据也被回滚掉了。

⑤ Propagation.NOT_SUPPORTED

步骤:

function A(){ // 有事务
 functionB(); // 无事务,代码正常,添加注解:@Transactional(propagation = Propagation.NOT_SUPPORTED)
 functionC(); // 无事务,代码抛异常,添加注解:@Transactional(propagation = Propagation.NOT_SUPPORTED)
}

结果:

数据库中有 B 和 C 两条数据。

说明:

Propagation.NOT_SUPPORTED 的意思是当前代码以非事务的方式运行,就算有事务,而已暂停当前事务。

结合上面的伪代码来看,虽然 A 是有事务的,但是 B 和 c 的注解为 NOT_SUPPORTED,意思是我这个方法是不需要加入事务中的,所以 B 和 C 是没有加入 A 的事务中去的,所以哪怕 B 抛出异常,由于不参会事务,所以 B 和 C 的数据都是不会回滚的,所以数据库中就会有 B 和 C 两条数据。

⑥ Propagation.NEVER

步骤:

function A(){ // 有事务
 functionB(); // 无事务,代码正常,添加注解:@Transactional(propagation = Propagation.NEVER)
 functionC(); // 无事务,代码抛异常,添加注解:@Transactional(propagation = Propagation.NEVER)
}

结果:

数据库中无数据,并且运行时报错

org.springframework.transaction.IllegalTransactionStateException: Existing transaction found for transaction marked with propagation 'never'

说明:

Propagation.NEVER 的意思是以非事务的方式去运行,如果有事务,则抛出异常。

上述伪代码中,A 是有事务的,但是 B 和 C 添加了 NEVER 的事务,意思是别给我加事务,我拒绝,你要是给我加了事务我就给你抛异常。

⑦ Propagation.NESTED

步骤:

function A(){ // 有事务
 insert one data;

 functionB(); // 有事务,代码正常,事务为:@Transactional(propagation = Propagation.NESTED)
 functionC(); // 有事务,代码抛异常,事务为:@Transactional(propagation = Propagation.NESTED)

 throw exception;
}

结果:

结果就是没有结果,因为 JPA 是不支持这种循环嵌套事务的,所以在运行的时候就会抛出异常:

org.springframework.transaction.NestedTransactionNotSupportedException: JpaDialect does not support savepoints - check your JPA provider's capabilities

说明:

Propagation.NESTED 的意思是如果外层没有事务,则内层会添加一个,而如果外层有事务,则内层会嵌套一层事务,即内层是有两层事务的。

四、其他

demo 的源码见: 【GitHub 地址】

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对我们的支持。

(0)

相关推荐

  • Spring事务传播属性和隔离级别详细介绍

    1 事务的传播属性(Propagation) 1) REQUIRED ,这个是默认的属性 Support a current transaction, create a new one if none exists. 如果存在一个事务,则支持当前事务.如果没有事务则开启一个新的事务. 被设置成这个级别时,会为每一个被调用的方法创建一个逻辑事务域.如果前面的方法已经创建了事务,那么后面的方法支持当前的事务,如果当前没有事务会重新建立事务. 2) MANDATORY Support a curren

  • Spring中事务传播行为的介绍

    传播行为定义关于客户端和被调用方法的事务边界.Spring定义了7种截然不同的传播行为. 1,PROPAGATION_MANDATORY,表示该方法必须运行在一个事务中.如果当前没有事务正在发生,则抛出异常. 2,PROPAGATION_NESTED,表示如果当前有一个事务正在运行当中,则该方法应该运行在一个嵌套事务中.被嵌套的事务可以独立于封装事务进行提交或回滚.如果封装事务不存在,则行为就像PROPAGATION_REQUIRED一样. 3,PROPAGATION_NEVER,表示当前方法不

  • Spring 事务隔离与事务传播的详解与对比

    Spring 事务隔离与事务传播的详解与对比 Spring是SSH中的管理员,负责管理其它框架,协调各个部分的工作.今天一起学习一下Spring的事务管理.Spring的事务管理分为声明式跟编程式.声明式就是在Spring的配置文件中进行相关配置:编程式就是用注解的方式写到代码里. Spring配置文件中关于事务配置总是由三个组成部分,分别是DataSource.TransactionManager和代理机制这三部分,无论哪种配置方式,一般变化的只是代理机制这部分. DataSource. Tr

  • 深入理解Spring事务的传播行为

    前言 本文主要介绍下Spring事务中的传播行为.事务传播行为是Spring框架独有的事务增强特性,他不属于的事务实际提供方数据库行为.这是Spring为我们提供的强大的工具箱,使用事务传播行可以为我们的开发工作提供许多便利. 下面话不多说了,来一起看看详细的介绍吧 事务传播行为介绍 Spring中的7个事务传播行为: |事务行为|说明 | |:--|:--| |PROPAGATION_REQUIRED | 支持当前事务,假设当前没有事务.就新建一个事务 | | PROPAGATION_SUPP

  • 深入理解Spring的事务传播行为

    前言 Spring在TransactionDefinition接口中规定了7种类型的事务传播行为.事务传播行为是Spring框架独有的事务增强特性,他不属于的事务实际提供方数据库行为.这是Spring为我们提供的强大的工具箱,使用事务传播行可以为我们的开发工作提供许多便利.但是人们对他的误解也颇多,你一定也听过"service方法事务最好不要嵌套"的传言.要想正确的使用工具首先需要了解工具.本文对七种事务传播行为做详细介绍,内容主要代码示例的方式呈现. 基础概念 1. 什么是事务传播行

  • Spring中的事务传播行为示例详解

    一.背景介绍 Spring 框架应该是每一个人 javaer 都必须接触和学习的技术,Spring 公司所提供的各种框架是 Java 开发行业可参考的重要标准之一. Spring 中有 7 种类型的事务传播行为.事务传播行为是 Spring 框架提供的一种事务管理方式,它是 Spring 框架之中非常重要的一个技术点,毕竟事务关系到应用程序和数据库的交互,而数据更是互联网行业最为重要的资源. 平时开发过程中事务都会有使用,但是没有真正地总结过,尤其是事务中嵌套事务的场景,此篇基础知识文在此做一个

  • Spring AOP事务管理的示例详解

    目录 转账案例-环境搭建 步骤1:准备数据库表 步骤2:创建项目导入jar包 步骤3:根据表创建模型类 步骤4:创建Dao接口 步骤5:创建Service接口和实现类 步骤6:添加jdbc.properties文件 步骤7:创建JdbcConfig配置类 步骤8:创建MybatisConfig配置类 步骤9:创建SpringConfig配置类 步骤10:编写测试类 事务管理 转账案例-环境搭建 步骤1:准备数据库表 之前我们在整合Mybatis的时候已经创建了这个表,可以直接使用 create

  • Spring中Bean的命名方式代码详解

    本文主要描述的是关于spring中bean的命名方式,通过简单实例向大家介绍了六种方式,具体如下. 一般情况下,在配置一个Bean时需要为其指定一个id属性作为bean的名称.id在IoC容器中必须是唯一的,此外id的命名需要满足xml对id的命名规范. 在实际情况中,id命名约束并不会给我们带来影响.但是如果用户确实希望用到一些特殊字符来对bean进行命名,那么可以使用bean的name属性来进行命名,name属性没有字符上的限制,几乎可以使用任何字符. 每个Bean可以有一个或多个id,我们

  • Spring中@Async注解实现异步调详解

    异步调用 在解释异步调用之前,我们先来看同步调用的定义:同步就是整个处理过程顺序执行,当各个过程都执行完毕,并返回结果. 异步调用则是只是发送了调用的指令,调用者无需等待被调用的方法完全执行完毕,继续执行下面的流程.例如, 在某个调用中,需要顺序调用 A, B, C三个过程方法:如他们都是同步调用,则需要将他们都顺序执行完毕之后,过程才执行完毕: 如B为一个异步的调用方法,则在执行完A之后,调用B,并不等待B完成,而是执行开始调用C,待C执行完毕之后,就意味着这个过程执行完毕了. 概述说明 Sp

  • Spring中自定义数据类型转换的方法详解

    目录 类型转换服务 实现Converter接口 实现ConverterFactory接口 实现GenericConverter接口 环境:Spring5.3.12.RELEASE. Spring 3引入了一个core.onvert包,提供一个通用类型转换系统.系统定义了一个SPI来实现类型转换逻辑,以及一个API来在运行时执行类型转换.在Spring容器中,可以使用这个系统作为PropertyEditor实现的替代,将外部化的bean属性值字符串转换为所需的属性类型.还可以在应用程序中需要类型转

  • Java中枚举类的用法示例详解

    目录 1.引入枚举类 2.实现枚举类 3.枚举类的使用注意事项 4.枚举的常用方法 5.enum细节 1.引入枚举类 Java 枚举是一个特殊的类,一般表示一组常量,比如一年的 4 个季节,一个年的 12 个月份,一个星期的 7 天,方向有东南西北等. Java 枚举类使用 enum 关键字来定义,各个常量使用逗号 , 来分割. 示例: enum Color { RED, GREEN, BLUE; } 2.实现枚举类 接下来我们来看一个一个简单的DEMO示例: /** * java枚举 */ p

  • Spring Data JPA注解Entity使用示例详解

    目录 1.JPA协议中关于Entity的相关规定 需要注意的是: 2.常用注解 2.1 JPA支持的注解 2.2 常用注解 3.联合主键 3.1 @IdClass 3.2 @Embeddable与@EmbeddedId注解使用 3.3 两者的区别是什么? 1.JPA协议中关于Entity的相关规定 (1)实体是直接进行数据库持久化操作的领域对象(即一个简单的POJO),必须通过@Entity注解进行标示. (2)实体必须有一个 public 或者 projected的无参数构造方法. (3)持久

  • Go语言中的字符串处理方法示例详解

    1 概述 字符串,string,一串固定长度的字符连接起来的字符集合.Go语言的字符串是使用UTF-8编码的.UTF-8是Unicode的实现方式之一. Go语言原生支持字符串.使用双引号("")或反引号(``)定义. 双引号:"", 用于单行字符串. 反引号:``,用于定义多行字符串,内部会原样解析. 示例: // 单行 "心有猛虎,细嗅蔷薇" // 多行 ` 大风歌 大风起兮云飞扬. 威加海内兮归故乡. 安得猛士兮守四方! ` 字符串支持转义

  • C++中#include头文件的示例详解

    fstream是C++ STL中对文件操作的合集,包含了常用的所有文件操作.在C++中,所有的文件操作,都是以流(stream)的方式进行的,fstream也就是文件流file stream. 最常用的两种操作为: 1.插入器(<<) 向流输出数据.比如说打开了一个文件流fout,那么调用fout<<"Write to file"<<endl;就表示把字符串"Write to file"写入文件并换行. 2.析取器(>>

  • Spring中的AutowireCandidateResolver的具体使用详解

    接口定义 用于推断一个特定的beanDefinition是否能作为指定依赖的候选者的策略接口 public interface AutowireCandidateResolver { // 默认情况下直接根据bd中的定义返回,如果没有进行特殊配置的话为true default boolean isAutowireCandidate(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) { return bdHolder.g

随机推荐