【Spring Boot】事务的隔离级别与事务的传播特性详解:如何在 Spring 中使用事务?不同隔离级别的区别?

这篇具有很好参考价值的文章主要介绍了【Spring Boot】事务的隔离级别与事务的传播特性详解:如何在 Spring 中使用事务?不同隔离级别的区别?。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


1 事务

1.1 事务简介与 mysql 中的事务使用

事务这个词在学习 MySQL 和多线程并发编程的时候,想必大家或多或少接触过。那么什么是事务呢?

事务是指一组操作作为一个不可分割的执行单元,要么全部成功执行,要么全部失败回滚。在数据库中,事务可以保证数据的一致性、完整性和稳定性,同时避免了数据的异常和不一致情况。常见的事务包括插入、更新、删除等数据库操作。事务的核心要素是ACID特性,即原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability)。

比如,常见的转账操作,以小明给小红转账100元为例,分为如下两个操作:

  1. 小明的账户 -100元;
  2. 小红的账户 +100元。

如果没有事务,第一步操作执行成功,而第二步执行失败,就会导致小明账户平白无故的扣款而小红账户没有收到款项的问题。因此,事务的存在是必要的,这一组操作要么全部执行成功,要么一起失败~
springboot事务级别,JavaEE编程之路,spring,spring boot,后端

在 MySQL 中,事务有三个重要的操作,分别为:开启事务、提交事务、回滚事务,对应的操作命令如下:

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

1.2 Spring 编程式事务(手动操作)

与 MySQL 操作事务类似,Spring 手动操作事务也需要三个重要的操作:开启事务(获取事务)、提交事务、回滚事务。

SpringBoot 内置了两个对象:

  • DataSourceTransactionManager ⽤来获取事务(开启事务)、提交或回滚事务的;
  • TransactionDefinition 是事务的属性,在获取事务的时候需要将TransactionDefinition 传递进去从⽽获得⼀个事务 TransactionStatus

实现代码如下:

@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private UserService userService;
    // 事务管理器
    @Autowired
    private DataSourceTransactionManager transactionManager;
    // 定义事务属性
    @Autowired
    private TransactionDefinition transactionDefinition;

    @RequestMapping("/add")
    public int add(UserInfo userInfo) {
        // 非空校验
        if (userInfo == null || !StringUtils.hasLength(userInfo.getUsername())
            || !StringUtils.hasLength(userInfo.getPassword())) {
            return 0;
        }
        // 1. 开始事务
        TransactionStatus transactionStatus =
                transactionManager.getTransaction(transactionDefinition);
        int result = userService.add(userInfo);
        System.out.println("添加: " + result);
//        // 2. 回滚事务
//        transactionManager.rollback(transactionStatus);
        // 3. 提交事务
        transactionManager.commit(transactionStatus);
        return result;
    }
}

从上述代码可以看出,虽然可以实现事务,但是操作很繁琐。因此,我们 常常使用另一种更简单的方式:基于注解的声明式事务。

1.3 Spring 声明式事务(自动操作)

相比手动操作事务来说,声明式事务非常简单,只需要在需要的方法上添加 @Transactional 注解,无需手动开启事务和提交事务。

示例代码如下:

@Transactional // 声明式事务(自动提交)
@RequestMapping("/insert")
public Integer insert(UserInfo userInfo) {
    // 非空校验
    if (userInfo == null || !StringUtils.hasLength(userInfo.getUsername())
            || !StringUtils.hasLength(userInfo.getPassword())) {
        return 0;
    }
    int result = userService.add(userInfo);
    return result;
}

对于 @Transactional 的几点说明:

  1. 该注解可以加在方法或者类上,若加在类上,则说明该类的所有公共方法可以自动的开启和提交事务 ,无论修饰方法还是类,都只对 public 方法有效;
  2. 在方法执行前自动开启事务,在方法执行完毕(没有发生任何异常)自动提交事务。如果 在方法执行期间出现异常,会自动回滚事务。

附:@Transactional 的常见参数:

参数 说明
propagation 定义了事务方法被嵌套调用时,事务如何传播到被调用的方法。常见取值包括:
- REQUIRED(默认):如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
- REQUIRES_NEW:每次调用方法时都会创建一个新的事务,如果存在当前事务,则将其挂起。
- SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务方式执行。
- NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,则将其挂起。
- MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
- NEVER:以非事务方式执行操作,如果当前存在事务,则抛出异常。
- NESTED:如果当前存在事务,则在嵌套事务内执行;如果当前没有事务,则创建一个新的事务。
isolation 定义了事务并发执行时,事务之间的隔离程度。常见取值包括:
- DEFAULT(默认):使用数据库默认的隔离级别。
- READ_UNCOMMITTED:最低的隔离级别,事务可以读取未提交的数据。
- READ_COMMITTED:事务只能读取已提交的数据。
- REPEATABLE_READ:事务在整个过程中保持一致的读取视图,防止脏读和不可重复读。
- SERIALIZABLE:最高的隔离级别,事务串行执行,避免脏读、不可重复读和幻读。
timeout 定义了事务执行的最长时间,单位为秒。默认值为-1,表示没有超时限制。
readOnly 如果设置为true,表示事务只读,不会修改数据库的数据。默认值为false
rollbackFor 触发事务回滚的异常类数组。当方法抛出指定的异常时,事务将回滚。
noRollbackFor 不触发事务回滚的异常类数组。当方法抛出指定的异常时,事务将不会回滚。
rollbackForClassName 触发事务回滚的异常类名数组。当方法抛出指定的异常时,事务将回滚。
noRollbackForClassName 不触发事务回滚的异常类名数组。当方法抛出指定的异常时,事务将不会回滚。
value 用于指定事务管理器的名称。如果应用程序中存在多个事务管理器,可以使用该参数指定要使用的事务管理器的名称。默认情况下,事务将使用默认的事务管理器。
transactionManager 用于指定事务管理器的引用。可以直接将一个事务管理器对象传递给该参数,以指定要使用的事务管理器。默认情况下,事务将使用默认的事务管理器。

对于上述表格中的事务隔离级别需要重点掌握,具体后面详细说。

需要特别注意的是,如果方法中的异常被 try-catch 异常捕获处理后,则不会再进行事务的回滚。

当然,我们可以通过 throw 将异常抛出,使得事务能够正常自动回滚。但是这样子做,try-catch 还有意义吗?springboot事务级别,JavaEE编程之路,spring,spring boot,后端

因此,对于这种情况,更偏向于使用另一种优雅的方式,进行手动回滚事务来解决~

如何在声明式事务中进行手动回滚事务?
使用代码进行手动回滚事务:

TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();

示例代码如下:
springboot事务级别,JavaEE编程之路,spring,spring boot,后端

1.4 @Transactional 的工作原理

  1. 当调用被@Transactional注解标记的方法时,事务管理器会检查当前是否存在一个事务。如果存在事务,则该方法将在该事务的上下文中执行;如果不存在事务,则会创建一个新的事务。
  2. 在方法执行期间,如果发生了受检查异常(checked exception),事务管理器会捕获该异常,并根据配置的回滚规则决定是否回滚事务。如果异常被捕获并且需要回滚事务,则事务将被回滚,方法的执行将终止,并将异常传播给调用方。
  3. 如果方法成功执行并且没有抛出受检查异常,事务管理器将提交事务,将数据库中的更改持久化。
  4. 如果方法执行期间抛出了未受检查异常(unchecked exception)或错误(Error),事务管理器会将事务标记为回滚,并将异常传播给调用方。
  5. 如果方法执行期间没有抛出异常,但在方法内部调用了其他被@Transactional注解标记的方法,事务管理器将根据事务的传播行为决定如何处理这些方法。例如,如果传播行为设置为REQUIRED,则内部方法将加入当前事务;如果传播行为设置为REQUIRES_NEW,则内部方法将创建一个新的事务。

具体来看,@Transactional 是基于 AOP 实现的,AOP ⼜是使⽤动态代理实现的。如果⽬标对象实现了接⼝,默认情况下会采⽤ JDK 的动态代理,如果⽬标对象没有实现了接⼝,会使⽤ CGLIB 动态代理。@Transactional 在开始执⾏业务之前,通过代理先开启事务,在执⾏成功之后再提交事务。如果中途遇到的异常,则回滚事务。实现细节的执行流程如图所示:
springboot事务级别,JavaEE编程之路,spring,spring boot,后端


2 事务的隔离级别

2.1 事务的四大特性及事务的隔离级别回顾

事务有4 ⼤特性(ACID),原⼦性、⼀致性、隔离性和持久性:

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

其中,对于隔离性有隔离级别可以设置。事务隔离分为不同级别,包括读未提交(Read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串⾏化(Serializable)。

归根到底,事务隔离级别的设置是为了防止其它事务影响当前事务的一种策略。

在MySQL中,默认是可重复读(repeatable read)级别。以下是MySQL常见的事务隔离级别以及它们对脏读、不可重复读和幻读问题的解决情况的表格:

事务隔离级别 脏读(Dirty Read) 不可重复读(Non-repeatable Read) 幻读(Phantom Read)
读未提交(Read Uncommitted) 可能发生 可能发生 可能发生
读已提交(Read Committed) 避免 可能发生 可能发生
可重复读(Repeatable Read) 避免 避免 可能发生
串行化(Serializable) 避免 避免 避免

对于脏读、不可重复读和幻读的解释:

  • 脏读(Dirty Read):指一个事务读取了另一个事务未提交的数据。如果一个事务可以读取未提交的数据,则会发生脏读。

  • 不可重复读(Non-repeatable Read):指在同一个事务中,多次读取同一行数据时,得到的结果不一致。这是因为在读取期间,另一个事务修改了该行数据。

  • 幻读(Phantom Read):指在同一个事务中,多次查询同一个范围的数据时,得到的结果集不一致。这是因为在查询期间,另一个事务插入或删除了符合查询条件的数据。

每个隔离级别对这些问题的解决情况如下:

  • 读未提交(Read Uncommitted):允许脏读、不可重复读和幻读。一个事务可以读取另一个事务未提交的数据。

  • 读已提交(Read Committed):避免脏读。一个事务只能读取已提交的数据。但是,可能发生不可重复读和幻读,因为在同一个事务中,另一个事务可能会修改数据。

  • 可重复读(Repeatable Read):避免脏读和不可重复读。在同一个事务中,多次读取同一行数据时,得到的结果是一致的。但是,可能发生幻读,因为在同一个事务中,另一个事务可能会插入或删除数据。

  • 串行化(Serializable):避免脏读、不可重复读和幻读。事务串行执行,保证了数据的一致性和完整性。

但隔离级别的提升会增加并发性能的开销,因为更高的隔离级别通常需要使用锁来实现。

在数据库中,可以使用如下语句来查询全局事务隔离级别和当前连接的事务隔离级别:

select @@global.tx_isolation,@@tx_isolation;

springboot事务级别,JavaEE编程之路,spring,spring boot,后端

2.2 Spring 事务的隔离级别及设置

在Spring框架中,事务的隔离级别可以使用@Transactional注解来设置。@Transactional注解可以应用在方法级别或类级别上,用于声明一个事务性方法或类。

Spring 框架支持以下五个事务隔离级别:

  1. DEFAULT(默认):使用底层数据库的默认隔离级别。对于大多数数据库来说,通常是READ_COMMITTED

  2. READ_UNCOMMITTED:读未提交。允许脏读、不可重复读和幻读。这是最低的隔离级别,一个事务可以读取另一个事务未提交的数据。

  3. READ_COMMITTED:读已提交。避免脏读。一个事务只能读取已提交的数据。但是,可能发生不可重复读和幻读,因为在同一个事务中,另一个事务可能会修改数据。

  4. REPEATABLE_READ:可重复读。避免脏读和不可重复读。在同一个事务中,多次读取同一行数据时,得到的结果是一致的。但是,可能发生幻读,因为在同一个事务中,另一个事务可能会插入或删除数据。

  5. SERIALIZABLE:串行化。避免脏读、不可重复读和幻读。事务串行执行,保证了数据的一致性和完整性,但是性能太低。

可以在@Transactional注解上使用isolation属性来设置事务的隔离级别。例如:

@Transactional(isolation = Isolation.READ_COMMITTED)
public void myTransactionalMethod() {
    // 事务性操作
}

需要注意的是,事务的隔离级别还受数据库本身支持的隔离级别的限制。如果数据库不支持某个特定的隔离级别,那么Spring框架将尽力使用最接近的隔离级别。


3 Spring 事务传播机制

3.1 初探事务的传播机制

事务的传播机制是用来定义事务在传播过程中的行为模式的一种机制。 Spring 事务传播机制定义了多个包含了事务的方法,相互调用时,事务是如何在这些方法进行传递的。

对比事务的隔离级别来看,如果说事务的隔离级别是保证多个并发事务执行的可控性的(稳定性),则 事务的传播机制就是保证一个事务在多个调用方法间的可控性的(稳定性)。

事务的隔离级别解决的是多个并发事务调用数据库的问题:
springboot事务级别,JavaEE编程之路,spring,spring boot,后端

事务的传播机制解决的是一个事务在多个节点(方法)中传递的问题:
springboot事务级别,JavaEE编程之路,spring,spring boot,后端
比如,方法 A 正常执行,完成了事务。但是,方法 B 发生了错误。那么,方法 A 进行的事务操作是否要回滚呢?这就是事务的传播机制需要解决的问题~

3.2 Spring 事务传播机制的分类及设置

在Spring框架中,事务传播机制用于定义在多个事务性方法相互调用时,事务如何传播和交互的规则。Spring框架提供了七种不同的事务传播行为:

  1. REQUIRED(需要有):如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。这是最常用的传播行为。

  2. SUPPORTS(可以有):如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务方式执行。

  3. MANDATORY(强制有):如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。

  4. REQUIRES_NEW:创建一个新的事务,并挂起当前事务(如果存在)。新创建的事务与当前事务完全独立。

  5. NOT_SUPPORTED:以非事务方式执行,并且挂起当前事务(如果存在)。

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

  7. NESTED:如果当前存在事务,则在嵌套事务中执行。嵌套事务是独立于当前事务的子事务,它可以独立地进行提交或回滚。如果当前没有事务,则创建一个新的事务。

这些事务传播行为可以通过@Transactional注解的propagation属性进行设置。例如:

@Transactional(propagation = Propagation.REQUIRED)
public void myTransactionalMethod() {
    // 事务性操作
}

需要注意的是,事务传播行为仅在方法之间的调用时才会生效,对于同一个方法内部的事务性操作,传播行为不会起作用。

如果将事务比作房子,以伴侣为例子理解(以下图片来自网络):
springboot事务级别,JavaEE编程之路,spring,spring boot,后端

3.3 嵌套事务(NESTED)和加入事务(REQUIRED )的区别

  1. 整个事务如果全部执行成功,二者的结果是一样的。
  2. 如果事务执行到一半失败了,那么加入事务整个事务会全部回滚;而嵌套事务会局部回滚,不会影响上一个方法中执行的结果。

写在最后

本文被 JavaEE编程之路 收录点击订阅专栏 , 持续更新中。
 以上便是本文的全部内容啦!创作不易,如果你有任何问题,欢迎私信,感谢您的支持!

springboot事务级别,JavaEE编程之路,spring,spring boot,后端文章来源地址https://www.toymoban.com/news/detail-635688.html

到了这里,关于【Spring Boot】事务的隔离级别与事务的传播特性详解:如何在 Spring 中使用事务?不同隔离级别的区别?的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Spring 事务的相关配置、传播行为、隔离级别及注解配置声明式事务

    目录 一、事务的相关配置 1. 添加测试标签 2. 添加对应方法 3. 测试 二、事务的传播行为 三、事务的隔离级别 四、注解配置声明式事务 1. 注册事务注解驱动 2. 加上注解 3. 配置类代替xml文件中的注解事务支持 4. 测试 往期专栏文章相关导读  1. Maven系列专栏文章 2. Mybatis系列专

    2024年02月08日
    浏览(44)
  • Spring事务的四大特性+事务的传播机制+隔离机制

    原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。 事务是一个原子操作, 由一系列动作组成。 组成一个事务的多个数据库操作是一个不可分割的原子单元 ,只有所有的操作执行成功,整个事务才提交。 事务中的任何一个数据库操作失败

    2024年01月20日
    浏览(39)
  • 聊一聊数据库事务的那些事(隔离级别,传播行为)

      我们平时使用事务的时候,可能脑子里面想到和事务有关的知识点无非就是,ACID,事务隔离级别那一套,使用的事务也就是是通过注解的形式,或者手动开启事务。更细致一点的问题或许没有深究下去,比如事务的传播行为,注解形式和手动事务的区别等,今天我们就这几

    2024年02月07日
    浏览(57)
  • MySQL的事务特性、事务特性保证和事务隔离级别

            事务是指要么所有的操作都成功执行,要么所有的操作都不执行的一组数据库操作。 一、MySQL提供了四个事务特性,即ACID:          1. 原子性(Atomicity) :一个事务中的所有操作要么全部提交成功,要么全部回滚失败,保证事务的原子性。          2. 一

    2024年02月03日
    浏览(41)
  • 事务——什么是事务,事务的特性,事务的隔离级别

            事务就是用户定义的一系列操作,这些操作可以视为一个完成的逻辑处理工作单元,要么全部执行,要么全部不执行,是不可分割的工作单元。 典型场景:银行转账 A 转账100元给B,A账户减少100元,B账户增加100元; 如果A转出失败或者B转入失败(任意一方失败)

    2024年02月10日
    浏览(51)
  • 58、事务的基本特性和隔离级别

    事务基本特性ACID分别是: 原子性 指的是一个事务中的操作要么全部成功,要么全部失败。 一致性 指的是数据库总是从一个一致性的状态转换到另外一个一致性的状态。比如A转账给B 100块钱,假设A只有90块,支付之前我们数据库里的数据都是符合约束的,但是如果事务执行成功

    2024年02月16日
    浏览(45)
  • javaee 事务 事务的特性 事务的并发问题 事务的隔离级别

    是并发控制的单元,是用户定义的一个操作序列。这些操作要么都做,要么都不做,是一个不可分割的工作单位。通过事务,sql 能将逻辑相关的一组操作绑定在一起,以便服务器 保持数据的完整性。事务通常是以begin/start transaction开始,以commit或rollback结束。Commint表示提交,

    2024年02月09日
    浏览(49)
  • 数据库事务的四大特性与事务的隔离级别

    概要: 事务的四个特性:原子性、一致性、隔离性、持久性 事务不隔离带来的问题:更新丢失、脏读、不可重复读、虚读(幻读)。其中更新丢失就是并发写,这是一定不允许的,因此一定要解决更新丢失问题。 事务隔离的级别:读未提交(1000)、读已提交(1100)、可重

    2023年04月09日
    浏览(53)
  • Spring的事务隔离级别

    Spring的事务隔离级别是用于控制事务并发访问数据库时的行为。Spring框架提供了五个事务隔离级别,分别是: 1. DEFAULT(默认):使用数据库默认的事务隔离级别。在大多数情况下,这等同于使用READ_COMMITTED级别。 2. READ_UNCOMMITTED(读取未提交数据):最低的隔离级别,允许一

    2024年02月09日
    浏览(42)
  • Spring事务隔离级别

    Spring事务隔离级别共有五种:DEFAULT、READ_UNCOMMITTED、READ_COMMITTED、REPEATBLE_READ、SERIALIZABLE。下面对这五个级别进行简单的介绍。 1 DEFAULT Spring中 默认 的事务隔离级别。以连接的数据库的事务隔离级别为准。 2 READ_UNCOMMITTED Spring事务 最弱 的隔离级别。一个事务可以读取到另一个

    2024年02月09日
    浏览(46)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包