2023.12.9 关于 Spring Boot 事务传播机制详解

目录

事务传播机制

七大事务传播机制

支持当前调用链上的事务

Propagation.REQUIRED

Propagation.SUPPORTS

Propagation.MANDATORY

不支持当前调用链上的事务

Propagation.REQUIRES_NEW

Propagation.NOT_SUPPORTED

Propagation.NEVER

嵌套事务

Propagation.NESTED

前置工作

初始化数据库

[初始化 实体类](#初始化 实体类)

[初始化 Mapper 接口](#初始化 Mapper 接口)

[初始化 XML 文件](#初始化 XML 文件)

重点理解部分

[NESTED 和 REQUIRED_NEW 的区别](#NESTED 和 REQUIRED_NEW 的区别)

[NESTED 和 REQUIRED 的区别](#NESTED 和 REQUIRED 的区别)


事务传播机制

  • 事务的传播机制是指在多个事务方法之间调用时,事务如何在这些方法之间传播

七大事务传播机制

支持当前调用链上的事务

Propagation.REQUIRED

  • 默认的事务传播级别
  • 如果当前没有事务,则新建一个事务,如果当前存在事务,则加入该事务

实例理解


Propagation.SUPPORTS

  • 如果当前方法没有事务,就以非事务方式执行,如果已经存在一个事务中,则加入到这个事务中

Propagation.MANDATORY

  • 如果当前方法没有事务,则抛出异常,如果已经存在一个事务中,则加入到这个事务中

不支持当前调用链上的事务

Propagation.REQUIRES_NEW

  • 创建一个新事务,如果存在当前事务,则挂起当前事务

Propagation.NOT_SUPPORTED

  • 以非事务方式执行,如果存在当前事务,则挂起当前事务

Propagation.NEVER

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

嵌套事务

Propagation.NESTED

  • 如果当前存在事务,则在嵌套事务中执行,否则与 REQUIRED 的操作一样

前置工作

  • 此处为了方便下文进行代码测试理解
  • 我们先将准备工作做好

初始化数据库

  • 创建一个 user 表,并添加几条用户信息

初始化 实体类

  • 创建 User 实体类 与 数据库的 user 表字段名相对应
java 复制代码
import lombok.Data;

@Data
public class User {
    private int id;
    private String name;
    private int age;
    private String password;
    private int state;
}

初始化 Mapper 接口

  • 初始化 UserMapper 接口,此处我们添加一个 add 方法
java 复制代码
import com.example.demo.entity.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

@Mapper
public interface UserMapper {
//    新增用户信息
    Integer add(User user);
}

初始化 XML 文件

  • 在与 UserMapper 接口相对应的 XML 文件中添加上与 add 方法 相对应的 sql 语句
XML 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.UserMapper">

    <insert id="add">
        insert into user(name,age,password) values(#{name},#{age},#{password})
    </insert>
</mapper>

重点理解部分

NESTED 和 REQUIRED_NEW 的区别

  • REQUIRED_NEW 是新建一个事务并且新开始的这个事务与原有事务无关
  • 而 NESTED 是当前存在事务时会开启一个嵌套事务
  • 在 NESTED 情况下,父事务回滚时,子事务也会回滚
  • 而 REQUIRED_NEW 情况下,原有事务回滚,不会影响新开启的事务

实例理解

  • 我们创建一个 userController 类,并给 addUser 方法加上 REQUIRED 类型的事务
  • 此时的 addUser 方法,将自动创建一个事务A
  • 注意我们在 addUser 中加入了一个算数异常
java 复制代码
import com.example.demo.entity.User;
import com.example.demo.service.LoggerService;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/user2")
public class UserController {
    @Autowired
    private UserService userService;

    @RequestMapping("/add")
    @Transactional(propagation = Propagation.REQUIRED)
    public String addUser(User user) {
//        调用 userService 的 add 方法
        int result = userService.add(user);
        int c = 10/0;
        return "add 方法:" + (result == 1 ? "新增用户成功": "新增用户失败");
    }
}
  • 可以看到 userController 类中调用了 add 方法,该方法在 userService 中
  • 此处我们给 add 方法加上 NESTED 类型的事务
  • 因为 addUser 已经创建了一个事务A,所以此处的 add 方法将在 事务A中创建一个嵌套事务
java 复制代码
import com.example.demo.entity.User;
import com.example.demo.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Service
public class UserService {

    @Autowired
    private UserMapper userMapper;

    //    新增用户信息
    @Transactional(propagation = Propagation.NESTED)
    public int add(User user) {
        int result = userMapper.add(user);
        return result;
    }
}

运行结果:

  • 在浏览器中输入对应的 URL 来访问 addUser 方法
  • 此时我们关注的是 在 NESTED 情况下,父事务回滚时,子事务也会回滚
  • 此处正好为 addUser 方法发生了 算数异常,从而进行了回滚操作
  • 且在抛出算数异常前,就已经调用了 userService.add 方法
  • 如果此处数据库中插入了新用户信息,则代表子事务未进行回滚操作
  • 此时我们查看 user 表,发现新用户未插入,即子事务进行了回滚操作,与预期结果一致

调整 add 方法的事务传播机制

  • 我们将 add 方法改为 REQUIRES_NEW 类型的事务
  • 因为 addUser 已经创建了一个事务A,所以此处的 add 方法将会把 事务A 挂起,另外创建一个 事务B
java 复制代码
import com.example.demo.entity.User;
import com.example.demo.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Service
public class UserService {

    @Autowired
    private UserMapper userMapper;

    //    新增用户信息
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public int add(User user) {
        int result = userMapper.add(user);
        return result;
    }
}

运行结果:

  • 在浏览器中输入对应的 URL 来访问 addUser 方法
  • 此时我们关注的是 REQUIRED_NEW 情况下,原有事务回滚,不会影响新开启的事务
  • 查看 user 表发现新增了一条用户信息,即新开启的事务B 未进行回滚,与预期结果一致

NESTED 和 REQUIRED 的区别

  • REQUIRED 的情况下,调用方存在事务时,则被调用发和调用方使用同一个事务
  • 那么被调用方出现异常时,由于共用同一个事务,所以无论是否 catch 异常,事务都会回滚
  • 而在 NESTTED 情况下,被调用方发生异常时,调用方可以 catch 其异常,这样只有子事务回滚,父事务不回滚

实例理解

  • 我们创建一个 userController 类,并给 addUser 方法加上 REQUIRED 类型的事务
  • 此时的 addUser 方法,将自动创建一个事务A
  • 我们将在 userService 的 add 方法中加入一个 算数异常
  • 为了验证 REQUIRED 的情况下, 被调用方出现异常时,由于共用同一个事务,所以无论是否 catch 异常,事务都会回滚
  • 如果不捕获 add 方法抛出的异常 事务A 肯定会进行回滚操作
  • 所以我们此处对算数异常进行捕获,由此来看 事务A 是否还会进行回滚操作
java 复制代码
import com.example.demo.entity.User;
import com.example.demo.service.LoggerService;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/user2")
public class UserController {
    @Autowired
    private UserService userService;

    @RequestMapping("/add")
    @Transactional(propagation = Propagation.REQUIRED)
    public String addUser(User user) {
        int result = 0;
        try {
//        调用 userService 的 add 方法
            result = userService.add(user);
        }catch (Exception e){
            e.printStackTrace();
        }
        return "add 方法:" + (result == 1 ? "新增用户成功": "新增用户失败");
    }
}
  • 此处我们给 add 方法加上 REQUIRED 类型的事务
  • 因为 addUser 已经创建了一个事务A,所以此处的 add 方法将会直接加入 事务A 中
  • 此处我们在 add 方法中加入了一个算数异常
java 复制代码
import com.example.demo.entity.User;
import com.example.demo.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

//添加 @Service 注解 代表该类会伴随着 项目的启动而注入到容器中
@Service
public class UserService {

    @Autowired
    private UserMapper userMapper;

    //    新增用户信息
    @Transactional(propagation = Propagation.REQUIRED)
    public int add(User user) {
        int result = userMapper.add(user);
        int a = 1/0;
        return result;
    }
}

运行结果:

  • 在浏览器中输入对应的 URL 来访问 addUser 方法
  • 查看 user 表发现没有新增新用户信息,即事务A进行了回滚操作,与预期结果一致

调整 add 方法的事务传播机制

  • 我们将 add 方法改为 NESTED 类型的事务
  • 因为 addUser 已经创建了一个事务A,所以此处的 add 方法将会在 事务A 中创建一个嵌套事务
java 复制代码
import com.example.demo.entity.User;
import com.example.demo.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

//添加 @Service 注解 代表该类会伴随着 项目的启动而注入到容器中
@Service
public class UserService {

    @Autowired
    private UserMapper userMapper;

    //    新增用户信息
    @Transactional(propagation = Propagation.NESTED)
    public int add(User user) {
        int result = userMapper.add(user);
        int a = 1/0;
        return result;
    }
}

运行结果:

  • 在浏览器中输入对应的 URL 来访问 addUser 方法
  • 此处为了验证 NESTTED 情况下,被调用方发生异常时,调用方可以 catch 其异常,这样只有子事务回滚,父事务不回滚
  • 在浏览器中输入对应的 URL 来访问 addUser 方法
  • 我们可以看到 事务A 未进行回滚操作
  • 查看 user 表发现没有新增新用户信息,即子事务进行了回滚操作,与预期结果一致
相关推荐
小吴编程之路17 小时前
MySQL 索引核心特性深度解析:从底层原理到实操应用
数据库·mysql
~莫子17 小时前
MySQL集群技术
数据库·mysql
HalvmånEver17 小时前
7.高并发内存池大页内存申请释放以及使用定长内存池脱离new
java·spring boot·spring
凤山老林17 小时前
SpringBoot 使用 H2 文本数据库构建轻量级应用
java·数据库·spring boot·后端
就不掉头发17 小时前
Linux与数据库进阶
数据库
与衫17 小时前
Gudu SQL Omni 技术深度解析
数据库·sql
咖啡の猫18 小时前
Redis桌面客户端
数据库·redis·缓存
oradh18 小时前
Oracle 11g数据库软件和数据库静默安装
数据库·oracle
赶路人儿18 小时前
UTC时间和时间戳介绍
java·开发语言
dreamread18 小时前
【SpringBoot整合系列】SpringBoot3.x整合Swagger
java·spring boot·后端