Spring事务、事务隔离级别、事务传播机制

article/2025/9/21 1:41:21

Spring事务、事务隔离级别、事务传播机制

  • 一、为什么需要事务?(回顾)
  • 二、Spring中事务的实现
    • 2.1 MySQL中的事务使用 (回顾)
    • 2.2 Spring编程式事务
    • 2.3 Spring 声明式事务
      • 2.3.1 @Transactional 使用
      • 2.3.2 @Transactional 作用范围
      • 2.3.3 @Transactional 参数说明
      • 2.3.4 注意事项
      • 2.3.4 @Transactional 工作原理
  • 三、事务隔离级别
    • 3.1 事务特性回顾
    • 3.2 Spring 中设置事务隔离级别
      • 3.2.1 MySQL 事务隔离级别有 4 种
      • 3.2.2 Spring 事务隔离级别有 5 种
  • 四、Spring事务传播机制
    • 4.1 事务传播机制是什么?
    • 4.2 为什么需要事务传播机制?
    • 4.3 事务传播机制有哪些?
    • 4.4 加入事务和嵌套事务有什么区别?

一、为什么需要事务?(回顾)

博客链接:https://blog.csdn.net/yyhgo_/article/details/128050926

事务定义: 将⼀组操作封装成⼀个执⾏单元(封装到⼀起),要么全部成功,要么全部失败。

为什么要用事务?
⽐如转账分为两个操作:

第⼀步操作:A 账户 -100 元。
第⼆步操作:B 账户 +100 元。

如果没有事务,第⼀步执⾏成功了,第⼆步执⾏失败了,那么 A 账户平⽩⽆故的 100 元就“⼈间蒸发”了。⽽如果使⽤事务就可以解决这个问题,让这⼀组操作要么⼀起成功,要么⼀起失败。

二、Spring中事务的实现

Spring 中的事务操作分为两类:

  1. 编程式事务(手动写代码操作事务)。
  2. 声明式事务(利用注解自动开启和提交事务)。

在开始讲解它们之前,咱们先来回顾事务在 MySQL 中是如何使⽤的?

2.1 MySQL中的事务使用 (回顾)

事务在 MySQL 有 3 个重要的操作:开启事务、提交事务、回滚事务,它们对应的操作命令如下:

-- 开启事务
start transaction;
-- 业务执⾏-- 提交事务
commit;-- 回滚事务
rollback;

2.2 Spring编程式事务

Spring ⼿动操作事务和上⾯ MySQL 操作事务类似,它也是有 3 个重要操作步骤:

  • 开启事务(获取事务)。
  • 提交事务。
  • 回滚事务。

SpringBoot 内置了两个对象,DataSourceTransactionManager ⽤来获取事务(开启事务)、提交或回滚事务的,⽽ TransactionDefinition 是事务的属性,在获取事务的时候需要将 TransactionDefinition 传递进去从⽽获得⼀个事务 TransactionStatus,实现代码如下:

@RestController
public class UserController {@Resourceprivate UserService userService;// JDBC 事务管理器@Resourceprivate DataSourceTransactionManager dataSourceTransactionManager;// 定义事务属性@Resourceprivate TransactionDefinition transactionDefinition;@RequestMapping("/sava")public Object save(User user) {// 开启事务TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);// 插⼊数据库int result = userService.save(user);// 提交事务dataSourceTransactionManager.commit(transactionStatus);
// // 回滚事务
// dataSourceTransactionManager.rollback(transactionStatus);return result;}
}

从上述代码可以看出,以上代码虽然可以实现事务,但操作也很繁琐,有没有更简单的实现⽅法呢?请看下⾯声明式事务。

2.3 Spring 声明式事务

2.3.1 @Transactional 使用

声明式事务的实现很简单,只需要在需要的方法上添加 @Transactional 注解就可以实现了,无需手动开启事务和提交事务,进入方法时自动开启事务,方法执行完会自动提交事务,如果中途发生了没有处理的异常会自动回滚事务, 具体实现代码如下:

    @RequestMapping("/save")@Transactionalpublic Object save(User user) {int result = userService.save(user);return result;}

接下⾥使⽤以下代码,分别设置 @Transactional 注解和不设置 @Transactional,观察它们的执⾏区别:
在这里插入图片描述

2.3.2 @Transactional 作用范围

@Transactional 可以⽤来修饰⽅法或类:

  • 修饰⽅法时:需要注意只能应用到 public 方法上,否则不生效。 推荐此种⽤法。
  • 修饰类时:表明该注解对该类中所有的 public ⽅法都⽣效。

2.3.3 @Transactional 参数说明

参数作用
value当配置了多个事务管理器时,可以使用该属性指定选择哪个事务管理器.
transactionManager当配置了多个事务管理器时,可以使用该属性指定选择哪个事务管理器.
propagation事务的传播行为,默认值为Propagation.REQUIRED
isolation事务的隔离级别,默认值为lsolation.DEFAULT
timeout事务的超时时间,默认值为-1.如果超过该时间限制但事务还没有完成,则自动回滚事务.
readOnly指定事务是否为只读事务,默认值为false;为了忽略那些不需要事务的方法,比如读取数据,可以设置read-only为 true.
rollbackFor用于指定能够触发事务回滚的异常类型,可以指定多个异常类型.
rollbackForClassName用于指定能够触发事务回滚的异常类型,可以指定多个异常类型.
noRollbackFor抛出指定的异常类型,不回滚事务,也可以指定多个异常类型.
noRollbackForClassName抛出指定的异常类型,不回滚事务,也可以指定多个异常类型.

2.3.4 注意事项

@Transactional 在异常被捕获的情况下,不会进行事务自动回滚, 验证以下代码是否会发⽣事务回滚:

    @RestControllerpublic class UserController {@Resourceprivate UserService userService;@RequestMapping("/save")@Transactionalpublic Object save(User user) {// 插⼊数据库int result = userService.save(user);try {// 执⾏了异常代码(0不能做除数)int i = 10 / 0;} catch (Exception e) {System.out.println(e.getMessage());}return result;}}

事务不会自动回滚解决方案:

解决⽅案1:对于捕获的异常,事务是会⾃动回滚的,因此解决⽅案1就是可以将异常重新抛出,具体实现如下:

    @RequestMapping("/save")@Transactional(isolation = Isolation.SERIALIZABLE)public Object save(User user) {// 插⼊数据库int result = userService.save(user);try {// 执⾏了异常代码(0不能做除数)int i = 10 / 0;} catch (Exception e) {System.out.println(e.getMessage());// 将异常重新抛出去throw e;}return result;}

解决⽅案2:⼿动回滚事务,在⽅法中使⽤ TransactionAspectSupport.currentTransactionStatus() 可以得到当前的事务,然后设置回滚⽅法 setRollbackOnly 就可以实现回滚了,具体实现代码如下:

    @RequestMapping("/save")@Transactional(isolation = Isolation.SERIALIZABLE)public Object save(User user) {// 插⼊数据库int result = userService.save(user);try {// 执⾏了异常代码(0不能做除数)int i = 10 / 0;} catch (Exception e) {System.out.println(e.getMessage());// ⼿动回滚事务TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();}return result;}

2.3.4 @Transactional 工作原理

@Transactional 是基于 AOP 实现的,AOP ⼜是使⽤动态代理实现的。如果⽬标对象实现了接⼝,默认情况下会采⽤ JDK 的动态代理,如果⽬标对象没有实现了接⼝,会使⽤ CGLIB 动态代理。
@Transactional 在开始执⾏业务之前,通过代理先开启事务,在执⾏成功之后再提交事务。如果中途遇到的异常,则回滚事务。

@Transactional 实现思路预览:

在这里插入图片描述

@Transactional 具体执⾏细节如下图所示:

在这里插入图片描述

三、事务隔离级别

3.1 事务特性回顾

事务有4 ⼤特性(ACID),原⼦性、持久性、⼀致性和隔离性,具体概念如下:

  • 原⼦性:⼀个事务(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执⾏过程中发⽣错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执⾏过⼀样。
  • ⼀致性:在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写⼊的资料必须完全符合所有的预设规则,这包含资料的精确度、串联性以及后续数据库可以⾃发性地完成预定的⼯作。
  • 持久性:事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。
  • 隔离性:数据库允许多个并发事务同时对其数据进⾏读写和修改的能⼒,隔离性可以防⽌多个事务并发执⾏时由于交叉执⾏⽽导致数据的不⼀致。事务隔离分为不同级别,包括读未提交 (Read uncommitted)、读提交 (read committed)、可重复读 (repeatable read) 和串⾏化 (Serializable)。

上⾯ 4 个属性,可以简称为 ACID
原⼦性(Atomicity,或称不可分割性)
⼀致性(Consistency)
隔离性(Isolation,⼜称独⽴性)
持久性(Durability)

⽽这 4 种特性中,只有隔离性(隔离级别)是可以设置的。

为什么要设置事务的隔离级别?
设置事务的隔离级别是⽤来保障多个并发事务执⾏更可控,更符合操作者预期的。

3.2 Spring 中设置事务隔离级别

Spring 中事务隔离级别可以通过 @Transactional 中的 isolation 属性进⾏设置,具体操作如下图所示:

在这里插入图片描述

3.2.1 MySQL 事务隔离级别有 4 种

  1. READ UNCOMMITTED:读未提交,也叫未提交读,该隔离级别的事务可以看到其他事务中未提交的数据。该隔离级别因为可以读取到其他事务中未提交的数据,⽽未提交的数据可能会发⽣回滚,因此我们把该级别读取到的数据称之为脏数据,把这个问题称之为脏读。
  2. READ COMMITTED:读已提交,也叫提交读,该隔离级别的事务能读取到已经提交事务的数据,因此它不会有脏读问题。但由于在事务的执⾏中可以读取到其他事务提交的结果,所以在不同时间的相同 SQL 查询中,可能会得到不同的结果,这种现象叫做不可重复读。
  3. REPEATABLE READ:可重复读,是 MySQL 的默认事务隔离级别,它能确保同⼀事务多次查询的结果⼀致。但也会有新的问题,⽐如此级别的事务正在执⾏时,另⼀个事务成功的插⼊了某条数据,但因为它每次查询的结果都是⼀样的,所以会导致查询不到这条数据,⾃⼰重复插⼊时⼜失败(因为唯⼀约束的原因)。明明在事务中查询不到这条信息,但⾃⼰就是插⼊不进去,这就叫幻读(Phantom Read)。
  4. SERIALIZABLE:序列化,事务最⾼隔离级别,它会强制事务排序,使之不会发⽣冲突,从⽽解决了脏读、不可重复读和幻读问题,但因为执⾏效率低,所以真正使⽤的场景并不多。

在这里插入图片描述

  • 脏读:⼀个事务读取到了另⼀个事务修改的数据之后,后⼀个事务⼜进⾏了回滚操作,从⽽导致第⼀个事务读取的数据是错误的。
  • 不可重复读:⼀个事务两次查询得到的结果不同,因为在两次查询中间,有另⼀个事务把数据修改了。
  • 幻读:⼀个事务两次查询中得到的结果集不同,因为在两次查询中另⼀个事务有新增了⼀部分数据。

在数据库中通过以下 SQL 查询全局事务隔离级别和当前连接的事务隔离级别:

select @@global.tx_isolation,@@tx_isolation;

以上 SQL 的执⾏结果如下:

在这里插入图片描述

即 MySQL 的隔离级别默认为 可重复读REPEATABLE READ (Oracle 默认为 读已提交READ COMMITTED)。

3.2.2 Spring 事务隔离级别有 5 种

⽽ Spring 中事务隔离级别包含以下 5 种:

  1. Isolation.DEFAULT:以连接的数据库的事务隔离级别为主。
  2. Isolation.READ_UNCOMMITTED:读未提交,可以读取到未提交的事务,存在脏读。
  3. Isolation.READ_COMMITTED:读已提交,只能读取到已经提交的事务,解决了脏读,存在不可重复读。
  4. Isolation.REPEATABLE_READ:可重复读,解决了不可重复读,但存在幻读 (MySQL默认级别)。
  5. Isolation.SERIALIZABLE:串⾏化,可以解决所有并发问题,但性能太低。

从上述介绍可以看出,相⽐于 MySQL 的事务隔离级别,Spring 的事务隔离级别只是多了⼀个Isolation.DEFAULT(以数据库的全局事务隔离级别为主)。

Spring 中事务隔离级别只需要设置 @Transactional ⾥的 isolation 属性即可,具体实现代码如下:

    @RequestMapping("/save")@Transactional(isolation = Isolation.SERIALIZABLE)public Object save(User user) {// 业务实现}

四、Spring事务传播机制

4.1 事务传播机制是什么?

Spring 事务传播机制定义了多个包含了事务的⽅法,相互调⽤时,事务是如何在这些⽅法间进⾏传递的。

4.2 为什么需要事务传播机制?

事务隔离级别是保证多个并发事务执⾏的可控性的(稳定性的),而事务传播机制是保证一个事务在多个调用方法间的可控性的(稳定性的)。

事务隔离级别解决的是多个事务同时调⽤⼀个数据库的问题,如下图所示:

在这里插入图片描述

而事务传播机制解决的是⼀个事务在多个节点(⽅法)中传递的问题,如下图所示:
在这里插入图片描述

4.3 事务传播机制有哪些?

Spring 事务传播机制包含以下 7 种:

  1. Propagation.REQUIRED:默认的事务传播级别,它表示如果当前存在事务,则加⼊该事务;如果当前没有事务,则创建⼀个新的事务。
  2. Propagation.SUPPORTS:如果当前存在事务,则加⼊该事务;如果当前没有事务,则以⾮事务的⽅式继续运⾏。
  3. Propagation.MANDATORY:(mandatory:强制性)如果当前存在事务,则加⼊该事务;如果当前没有事务,则抛出异常。
  4. Propagation.REQUIRES_NEW:表示创建⼀个新的事务,如果当前存在事务,则把当前事务挂起。也就是说不管外部⽅法是否开启事务,Propagation.REQUIRES_NEW 修饰的内部⽅法会新开启⾃⼰的事务,且开启的事务相互独⽴,互不⼲扰。
  5. Propagation.NOT_SUPPORTED:以⾮事务⽅式运⾏,如果当前存在事务,则把当前事务挂起。
  6. Propagation.NEVER:以⾮事务⽅式运⾏,如果当前存在事务,则抛出异常。
  7. Propagation.NESTED:如果当前存在事务,则创建⼀个事务作为当前事务的嵌套事务来运⾏;如果当前没有事务,则该取值等价于 PROPAGATION_REQUIRED。

以上 7 种传播⾏为,可以根据是否⽀持当前事务分为以下 3 类:

在这里插入图片描述

以情侣关系为例来理解以上分类:
在这里插入图片描述

4.4 加入事务和嵌套事务有什么区别?

REQUIRED 和 NESTED 演示。

加入事务:
UserController:

    @RequestMapping("/add2")@Transactional(propagation = Propagation.REQUIRED)public int add2(String username, String password) {// 非空效验if (!StringUtils.hasLength(username) || !StringUtils.hasLength(password)) {return 0;}int result = userService.add(username, password, null);System.out.println("添加影响行数:" + result);int result2 = userService.save(username, password, null);System.out.println("添加影响行数:" + result2);return result;}

UserService:

    @Transactional(propagation = Propagation.REQUIRED)public int add(String username, String password, String photo) {return userMapper.add(username, password, photo);}@Transactional(propagation = Propagation.REQUIRED)public int save(String username, String password, String photo) {try {int result = 10 / 0;} catch (Exception e) {System.out.println("ex:" + e.getMessage());TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();}return userMapper.add(username, password, photo);}

最终数据库一行数据都没有添加。


嵌套事务:
修改 UserService:

    @Transactional(propagation = Propagation.REQUIRED)public int add(String username, String password, String photo) {return userMapper.add(username, password, photo);}@Transactional(propagation = Propagation.NESTED)public int save(String username, String password, String photo) {try {int result = 10 / 0;} catch (Exception e) {System.out.println("ex:" + e.getMessage());TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();}return userMapper.add(username, password, photo);}

最终数据库只添加了一行数据 (嵌套事务不影响整个事务)。

嵌套事务之所以能够实现部分事务的回滚,是因为事务中有⼀个保存点(savepoint)的概念。嵌套事务进⼊之后相当于新建了⼀个保存点,⽽滚回时只回滚到当前保存点,因此之前的事务是不受影响的。这⼀点可以在 MySQL 的官⽅⽂档汇总找到相应的资料:https://dev.mysql.com/doc/refman/5.7/en/savepoint.html
而 REQUIRED 是加⼊到当前事务中,并没有创建事务的保存点,因此出现了回滚就是整个事务回滚,这就是加⼊事务和嵌套事务的区别。

  • 整个事务如果全部执⾏成功,⼆者的结果是⼀样的。
  • 如果事务执⾏到⼀半失败了,那么加⼊事务整个事务会全部回滚;⽽嵌套事务会局部回滚,不会影响上⼀个⽅法中执⾏的结果。

http://chatgpt.dhexx.cn/article/o5gogGkG.shtml

相关文章

Spring 事务和事务的传播机制

1.Spring 中事务的实现方式 Spring 中的操作主要分为两类: 编程式事务 (了解) 声明式事务 编程式事务就是手写代码操作事务, 而声明式事务是利用注解来自动开启和提交事务. 并且编程式事务用几乎不怎么用. 这就好比汽车的手动挡和自动挡, 如果有足够的的钱, 大部分人应该都会选…

【Spring Boot】事务和事务传播机制

文章目录 1. 事务简单介绍2. Spring 中事务的实现2.1 Spring 手动操作事务2.2 Spring 声明式事务 3. Transactional 注解介绍3.1 Transactional 作用范围3.2 Transactional 参数说明3.3 Transactional 出现异常注意事项3.4 Transactional 工作原理 4. 事务隔离级别4.1 事务特性…

SpringBoot事务传播机制

Spring的事务传播机制:是指规定当程序中出现了多个方法出现了嵌套调用的时候,事务是如何进行传递的 支持当前事务:要有房子的 不支持当前事务:不许要有房子的 嵌套事务 1)定义:咱们之前所说的事务,都是针对一个方法的,咱们的Spring事务传播机…

Spring的事务传播机制(通俗易懂)

概述 Spring的事务传播机制有7种,在枚举Propagation中有定义。 1.REQUIRED PROPAGATION_REQUIRED:如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,该设置是最常用的默认设置。 Tra…

Spring中的事务传播机制

目录 前言 1、Spring(Spring Boot)实现事务 1.1、通过代码的方式手动实现事务 1.2、通过注解Transactional的方式实现声明式事务 1.2.1、实现: 1.2.2、程序中有try-catch时,程序发生异常事务不会回滚 解决方案一:将异常抛出去 …

事务的7种传播机制和演示

文章目录 一、事务的传播机制1.1、nested 事务的几点说明: 二、示例2.1、前言:2.2、准备测试方法1)创建beans.xml,开启事务2)创建实体类和表(表创建读者可自定义创建)3)创建service接…

事务传播行为

原文作者:https://blog.csdn.net/soonfly/article/details/70305683 事务传播行为 什么叫事务传播行为?听起来挺高端的,其实很简单。 即然是传播,那么至少有两个东西,才可以发生传播。单体不存在传播这个行为。 事务传…

[事务] 事务的传播机制

前言: Spring的事务,也就是数据库的事务操作,符合ACID标准,也具有标准的事务隔离级别。 但是Spring事务有自己的特点,也就是事务传播机制。 所谓事务传播机制,也就是在事务在多个方法的调用中是如何传递的&…

事物的传播机制

目录 1、事务的传播机制 2、测试 2.1、准备测试方法 2.2、事务传播机制的测试 2.2.1、REQUIRED 2.2.2、NOT_SUPPORTED 2.2.3、REQUIRES_NEW 2.2.4、MANDATORY 2.2.5、NEVER 2.2.6、SUPPORTS 2.2.7、NESTED 事务传播机制:就是事务在多个方法的调用中是如何…

Spring事务传播机制

目录 一、事务在Spring中是如何运作的 1.1 开启事务(DataSourceTransactionManager.doBegin) 二、Spring的事务传播机制 2.1 子事务的传播机制为REQUIRED 2.2 子事务的传播机制为REQUIRES_NEW 2.3 子事务的传播机制为NESTED 当我们在使用Spring所提供的事务功能时&#x…

Spring事务传播的7种机制

Spring 事务传播机制包含以下 7 种: 1. Propagation.REQUIRED:默认的事务传播级别,它表示如果当前存在事务,则加入该事务;如果 当前没有事务,则创建一个新的事务。 2. Propagation.SUPPORTS:如果…

事务的传播机制

目录 1.形象说明: 2.代码演示: 2.1 REQUIRED 2.1.1 验证共用一个事务 2.1.2 验证当前没有事务,就新建一个事务 2.2 SUPPORTS 2.2.1 支持使用当前事务 2.2.2 如果当前事务不存在,则不使用事务 2.3 MANDATORY 2.3.1 支持…

Spring事务传播机制详解

前言: Spring的事务,也就是数据库的事务操作,符合ACID标准,也具有标准的事务隔离级别。 但是Spring事务有自己的特点,也就是事务传播机制。 所谓事务传播机制,也就是在事务在多个方法的调用中是如何传递的&…

反射原理详谈

什么是反射? 反射是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为 Java 语言…

Java反射的作用与原理

Java反射的作用与原理 定义 反射机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。在Java中,只要给定类…

彻底搞懂java反射技术及其原理

概述:反射是java中最强大的技术之一,很多高级框架都用到了反射技术,面试中也是经常问的点,所以搞懂反射非常重要! 文章目录 1.反射是什么?2.反射的底层原理3.三种方式获取Class对象4.反射的优缺点5.反射的应用场景6.反射的常用API 1.反射是什么? java反射机制指…

java反射原理-重要

一,反射是什么(反射是框架设计的灵魂) 1,JAVA反射机制是在运行状态中 对于任意一个类,都能够知道这个类的所有属性和方法; 对于任意一个对象,都能够调用它的任意一个方法和属性; …

java 反射机制原理 简述

什么是反射机制? 1、在运行状态中,对于任意一个类,都能够知道这个类的属性和方法。 2、对于任意一个对象,都能够调用它的任何方法和属性。这种动态获取信息以及动态调用对象的方法的功能称为JAVA的反射。 反射的作用 1、在运行…

java反射如何实现的_Java反射实现原理

Java反射应用十分广泛,例如spring的核心功能控制反转IOC就是通过反射来实现的,本文主要研究一下发射方法调用的实现方式和反射对性能的影响。 如下为Method类中invoke方法,可以看出该方法实际是将反射方法的调用委派给MethodAccessor&#xf…

Java反射原理与使用

当类加载器将类加载进jvm之后,jvm会创建每一个类的元数据对象(Class),这个元数据对象(Class)记录着这类的所有信息,java语言允许通过元数据对象动态的创建对象实例,这种机制就称为java的反射机制,基本上所有框架的底层都用到了反射机制,spring、mybatis、servlet都用到了 1.如…