笔记 笔记
首页
  • 开发工具
  • Java Web
  • Java 进阶
  • 容器化技术
  • Java 专栏

    • Java 核心技术面试精讲
    • Java 业务开发常见错误 100 例
  • 数据库专栏

    • MySQL 实战 45 讲
    • Redis 核心技术与实战
  • 安全专栏

    • OAuth 2.0 实战课
  • 计算机系统
  • 程序设计语言
  • 数据结构
  • 知识产权
  • 数据库
  • 面向对象
  • UML
  • 设计模式
  • 操作系统
  • 结构化开发
  • 软件工程
  • 计算机网络
  • 上午题错题
在线工具 (opens new window)

EasT-Duan

Java 开发
首页
  • 开发工具
  • Java Web
  • Java 进阶
  • 容器化技术
  • Java 专栏

    • Java 核心技术面试精讲
    • Java 业务开发常见错误 100 例
  • 数据库专栏

    • MySQL 实战 45 讲
    • Redis 核心技术与实战
  • 安全专栏

    • OAuth 2.0 实战课
  • 计算机系统
  • 程序设计语言
  • 数据结构
  • 知识产权
  • 数据库
  • 面向对象
  • UML
  • 设计模式
  • 操作系统
  • 结构化开发
  • 软件工程
  • 计算机网络
  • 上午题错题
在线工具 (opens new window)

购买兑换码请添加

添加时候请写好备注,否则无法通过。

  • Maven

  • Bootstrap

  • Spring

    • Spring 概述
    • Spring 的入门
    • Spring 的工厂类
    • Spring 的 Bean
    • Spring 的依赖注入(DI)
    • 动态代理
    • Spring AOP
    • Spring 的事务管理
      • 什么是事务
      • 事务的特征
      • 如果不考虑隔离性引发的问题
        • 问题
        • 解决
      • Spring 的传播行为
      • @Transactional 注解简介
  • Spring MVC

  • MyBatis

  • JUnit

  • GitFlow 工作流指南

  • SpringBoot

  • Reactor

  • 微服务

  • Java Web
  • Spring
EasT-Duan
2023-06-08
目录

Spring 的事务管理

欢迎来到我的 ChatGPT 中转站,极具性价比,为付费不方便的朋友提供便利,有需求的可以添加左侧 QQ 二维码,另外,邀请新用户能获取余额哦!最后说一句,那啥:请自觉遵守《生成式人工智能服务管理暂行办法》。

# Spring 的事务管理

# 什么是事务

事务:逻辑上的一组操作,组成这个操作的各个单元,要么全部成功,要么全部失败

# 事务的特征

事务具有以下特性(通常使用 ACID 缩写来表示):

  1. 原子性(Atomicity):事务是不可分割的操作单位,要么全部成功执行,要么全部回滚。如果事务中的任何操作失败,将回滚到事务开始前的状态,不会留下部分完成的结果。
  2. 一致性(Consistency):事务在执行过程中要保持数据的一致性。这意味着事务的操作必须满足预定义的规则和约束,以确保数据的完整性和有效性。
  3. 隔离性(Isolation):事务的执行应该与其他并发事务相互隔离,使每个事务感觉就像是在独立地执行。隔离性确保并发事务之间不会互相干扰,防止数据损坏或读取脏数据。
  4. 持久性(Durability):一旦事务成功提交,其所做的更改应该是永久性的,即使在系统故障或崩溃之后也不会丢失。

# 如果不考虑隔离性引发的问题

# 问题

读问题:

  1. 脏读(Dirty Read):一个事务读取到了另一个并发事务未提交的数据,如果未提交的事务回滚,读取到的数据就是无效的。
  2. 不可重复读(Non-repeatable Read):一个事务在同一查询中多次读取同一行数据,但在这期间另一个并发事务对该行数据进行了修改或删除,导致读取到不一致的数据。
  3. 幻读(Phantom Read):一个事务在同一查询中多次读取满足某个条件的数据,但在这期间另一个并发事务插入或删除了满足该条件的数据,导致读取到了新增或删除的数据。

写问题:

  1. 丢失更新(Lost Update):两个或多个并发事务同时读取同一数据并进行修改,但只有最后一个提交的事务的修改结果会被保留,其他事务的修改结果会丢失。
  2. 脏写(Dirty Write):一个事务在未提交之前,另一个并发事务读取到了其修改但未提交的数据。如果未提交的事务回滚,读取到的数据就是无效的。
  3. 并发更新问题(Concurrency Update Problem):并发事务同时更新相同的数据,可能会导致数据的混乱和不一致。

# 解决

设置事务的隔离级别

  1. Read Uncommitted(读未提交):在这个隔离级别下,一个事务可以读取另一个事务尚未提交的数据,可能会导致脏读问题。这个隔离级别无法解决任何问题,因为它允许读取到未提交的临时数据。
  2. Read Committed(读已提交):在这个隔离级别下,一个事务只能读取到已经提交的数据,避免了脏读问题。然而,由于其他并发事务可能在事务执行期间提交新的数据,因此不可重复读和幻读问题仍有可能发生。
  3. Repeatable Read(重复读):在这个隔离级别下,一个事务在同一个查询中多次读取同一行数据时,保证每次读取到的数据是一致的,避免了不可重复读问题。但是,其他并发事务可能在事务执行期间插入新的数据或删除已有数据,导致幻读问题。
  4. Serializable(可串行化):在这个隔离级别下,事务串行执行,即每个事务都独占所访问的数据,避免了脏读、不可重复读和幻读问题。这是最高的隔离级别,但也是最严格的,可能导致并发性能下降。

# Spring 的传播行为

  1. REQUIRED(默认):如果当前存在事务,则方法在该事务中运行;如果当前没有事务,则创建一个新事务并在其中运行方法。该传播行为确保方法始终在事务中运行,如果调用方法的地方没有事务,则会为方法创建新的事务。如果在外部事务中调用多个有 REQUIRED 传播行为的方法,它们将在同一个事务中运行,如果有异常抛出,将会回滚整个事务。

    @Transactional(propagation = Propagation.REQUIRED)
    public void methodA() {
     methodB();
    // do something
    }
    
    @Transactional(propagation = Propagation.REQUIRED)
    public void methodB() {
        // do something
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    • 单独调用 methodB 方法时,因为当前上下文不存在事务,所以会开启一个新的事务。
    • 调用 methodA 方法时,因为当前上下文不存在事务,所以会开启一个新的事务。当执行到 methodB 时,methodB 发现当前上下文有事务,因此就加入到当前事务中来。
  2. SUPPORTS:如果当前存在事务,则方法在该事务中运行;如果当前没有事务,则方法以非事务方式运行。该传播行为允许方法在有事务的上下文中运行,也允许方法在没有事务的上下文中以非事务方式运行。它适用于读取或查询类型的操作,不需要强制要求在事务中执行。

    @Transactional(propagation = Propagation.REQUIRED)
    public void methodA() {
     methodB();
    // do something
    }
    
    // 事务属性为SUPPORTS
    @Transactional(propagation = Propagation.SUPPORTS)
    public void methodB() {
        // do something
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    • 单纯的调用 methodB 时,methodB 方法是非事务的执行的。当调用 methdA 时,methodB 则加入了 methodA 的事务中,事务执行。
  3. MANDATORY:如果当前存在事务,则方法在该事务中运行;如果当前没有事务,则抛出异常。该传播行为要求方法在一个已经存在的事务中运行,如果没有事务存在,则会抛出异常。它用于需要在事务上下文中执行的关键操作,确保调用方提供了有效的事务。

    @Transactional(propagation = Propagation.REQUIRED)
    public void methodA() {
     methodB();
    // do something
    }
    
    // 事务属性为MANDATORY
    @Transactional(propagation = Propagation.MANDATORY)
    public void methodB() {
        // do something
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    • 当单独调用 methodB 时,因为当前没有一个活动的事务,则会抛出异常 throw new IllegalTransactionStateException(“Transaction propagation ‘mandatory’ but no existing transaction found”) ;
    • 当调用 methodA 时,methodB 则加入到 methodA 的事务中,事务执行。
  4. REQUIRES_NEW:每次方法都会在自己的事务中运行。如果当前存在事务,则将其挂起,并在方法执行完毕后恢复。该传播行为要求方法在自己的事务中运行,无论当前是否已存在事务。它适用于需要独立于外部事务运行的方法,即使外部事务失败或回滚,方法的事务也会继续进行。

    @Transactional(propagation = Propagation.REQUIRED)
    public void methodA() {
    doSomeThingA();
    methodB();
    doSomeThingB();
    // do something else
    }
    
    
    // 事务属性为REQUIRES_NEW
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void methodB() {
        // do something
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    • 单独执行 methodB 和使用 Propagation.REQUIRES 效果是一致的。
    • 如果 methodA 方法在调用 methodB 方法后的 doSomeThingB 方法失败了,而 methodB 方法所做的结果依然被提交。而除了 methodB 之外的其它代码导致的结果却被回滚了
  5. NOT_SUPPORTED:方法以非事务方式运行,如果当前存在事务,则将其挂起。该传播行为要求方法以非事务方式运行,即使当前存在事务。它适用于不需要事务支持的操作,例如纯读取操作或与外部事务无关的方法。

    用图片说明就是

    @Transactional(propagation = Propagation.REQUIRED)
    public void methodA() {
    doSomeThingA();
     // 挂起事务
    methodB();
     // 恢复事务
    doSomeThingB();
    }
    
    
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public void methodB() {
        // do something
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    • 单独执行 methodB 就相当于没有事务
    • 当执行 methodA 的时候,在执行到 methodB 方法时候会停止事务,当 methodB 方法执行完成事务恢复。
  6. NEVER:方法以非事务方式运行,如果当前存在事务,则抛出异常。该传播行为要求方法在没有事务的上下文中运行,如果当前存在事务,则会抛出 IllegalTransactionStateException 异常。它用于确保方法不会在事务环境中运行。

    @Transactional(propagation = Propagation.NEVER)
    public void methodA() {
        doSomeThingA();
         // 挂起事务
        methodB();	// 不会发生异常
         // 恢复事务
        doSomeThingB();
        methodC(); // 发生异常
    }
    
    
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public void methodB() {
        // do something
    }
    
    @Transactional(propagation = Propagation.REQUIRED)
    public void methodC() {
        // do something
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    • 这个要说明的是,在上述的情况中是不会发生异常的,因为 NOT_SUPPORTED 会在 methodB 执行的时候挂起事务。
  7. NESTED:如果当前存在事务,则在嵌套事务中运行方法;如果当前没有事务,则创建一个新事务并在其中运行方法。嵌套事务是外部事务的一部分,具有独立的保存点,并且可以单独提交或回滚。如果外部事务回滚,嵌套事务也将回滚;如果外部事务提交,嵌套事务可以选择提交或回滚。它用于需要独立的保存点和部分提交 / 回滚的场景。

    // 设置保存点和回滚
    @Service
    @Transactional
    public class MyService {
    
        @Autowired
        private TransactionTemplate transactionTemplate;
    
        public void myMethod() {
            transactionTemplate.execute(status -> {
                try {
                    // 执行一些事务操作
                    // ...
    
                    // 创建保存点
                    Object savepoint = status.createSavepoint();
    
                    // 执行更多的事务操作
                    // ...
    
                    if (发生某些异常条件) {
                        // 回滚到保存点
                        status.rollbackToSavepoint(savepoint);
                    }
    
                    // 继续执行事务操作
                    // ...
    
                    return null;
                } catch (Exception e) {
                    // 处理异常
                    throw new RuntimeException("事务执行失败", e);
                }
            });
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    @Transactional(propagation = Propagation.REQUIRED)
    methodA(){
      doSomeThingA();
      methodB();
      doSomeThingB();
    }
    
    @Transactional(propagation = Propagation.NEWSTED)
    methodB(){
      ……
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

    • 当 methodB 方法调用之前,调用 setSavepoint 方法,保存当前的状态到 savepoint。如果 methodB 方法调用失败,则恢复到之前保存的状态。
    • 但是需要注意的是,这时的事务并没有进行提交,如果后续的代码 (doSomeThingB () 方法) 调用失败,则回滚包括 methodB 方法的所有操作。嵌套事务一个非常重要的概念就是内层事务依赖于外层事务。外层事务失败时,会回滚内层事务所做的动作。
    • 而内层事务操作失败并不会引起外层事务的回滚。

# @Transactional 注解简介

@Transactional 的所有可选属性:

  • propagation:用于设置事务传播属性。该属性类型为 Propagation 枚举,默认值为 Propagation.REQUIRED。
  • isolation:用于设置事务的隔离级别。该属性类型为 Isolation 枚举 ,默认值为 Isolation.DEFAULT。
  • readOnly:用于设置该方法对数据库的操作是否是只读的。该属性为 boolean,默认值为 false。
  • timeout:用于设置本操作与数据库连接的超时时限。单位为秒,类型为 int,默认值为 -1,即没有时限。
  • rollbackFor:指定需要回滚的异常类。类型为 Class [],默认值为空数组。当然,若只有一个异常类时,可以不使用数组。
  • rollbackForClassName:指定需要回滚的异常类类名。类型为 String [],默认值为空数组。当然,若只有一个异常类时,可以不使用数组。
  • noRollbackFor:指定不需要回滚的异常类。类型为 Class [],默认值为空数组。当然,若只有一个异常类时,可以不使用数组。
  • noRollbackForClassName: 指定不需要回滚的异常类类名。类型为 String [],默认值为空数组。当然,若只有一个异常类时,可以不使用数组。

注意

  1. 若 @Transactional 若用在方法上,只能用于 public 方法上。对于其他非 public 方法,如果加上了注解 @Transactional,虽然
    Spring 不会报错,但不会将指定事务织入到该方法中。因为 Spring 会忽略掉所有非 public 方法上的 @Transaction 注解。
  2. 若 @Transaction 注解在类上,则表示该类上所有的方法均将在执行时织入事务。
#Spring
上次更新: 2025/04/12, 07:54:33
Spring AOP
什么是三层架构

← Spring AOP 什么是三层架构→

最近更新
01
Reactor 核心
02-24
02
前置条件
10-30
03
计算机网络
09-13
更多文章>
Theme by Vdoing | Copyright © 2019-2025 powered by Vdoing
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式