当前位置: 首页 > news >正文

Spring事务系列 三

Spring事务的传播机制


Spring事务系列 一-CSDN博客

Spring事务系列 二-CSDN博客


文章目录

  • 系列文章目录

    目录

    文章目录

    前言

    一、Spring事务的传播机制

    Propagation.REQUIRED:

    Propagation.SUPPORTS:

    Propagation.MANDATORY:

    Propagation.REQUIRES_NEW:

    Propagation.NOT_SUPPORTED:

    Propagation.NEVER:

    Propagation.NESTED:

    二、代码演示

    1.准备工作

    2.Propagation.REQUIRED

    3.Propagation.REQUIRES_NEW

    4.Propagation.NEVER

    5.Propagation.NESTED

    6.Propagation.REQUIRED和Propagation.NESTED区别

    总结


前言

当多个事务存在调用关系,事务在这些方法间是如何传播的?本文介绍Spring事务的传播机制。


提示:以下是本篇文章正文内容,下面案例可供参考

一、Spring事务的传播机制

Spring可以设置@Transactional注解的propagation属性,来指定Spring事务的传播机制。

Spring事务传播机制有7种:

public enum Isolation {
    DEFAULT(-1),
    READ_UNCOMMITTED(1),
    READ_COMMITTED(2),
    REPEATABLE_READ(4),
    SERIALIZABLE(8);

    private final int value;

    private Isolation(int value) {
        this.value = value;
    }

    public int value() {
        return this.value;
    }
}

Propagation.REQUIRED:

默认的事务传播级别;

假设调用方存在事务,则加入该事务;

如果调用方没有事务,则创建一个新的事务。

Propagation.SUPPORTS:

如果调用方存在事务,则加入该事务;

如果调用方没有事务,则以非事务的方式继续运行;

Propagation.MANDATORY:

强制要求调用方必须存在事务;

如果调用方存在事务,则加入事务;

如果调用方没有事务,则抛出异常;

Propagation.REQUIRES_NEW:

如果调用方有事务,则挂起,并创建新的事务;两个事务相互独立,互不干扰;

不管调用方是否存在事务,都会创建一个新的事务;

Propagation.NOT_SUPPORTED:

如果调用方存在事务,则挂起,以非事务的方式运行;

不管调用方是否存在事务,都会以非事务的方式运行;

Propagation.NEVER:

以非事务方式运行,如果调用方当前存在事务,则抛出异常;

Propagation.NESTED:

如果调用方存在事务,则创建一个事务作为当前事务的嵌套事务来运行;

如果调用方没有事务,则创建一个新的事务;

二、代码演示

1.准备工作

<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter</artifactId>
			<version>3.0.4</version>
		</dependency>

		<dependency>
			<groupId>com.mysql</groupId>
			<artifactId>mysql-connector-j</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter-test</artifactId>
			<version>3.0.4</version>
			<scope>test</scope>
		</dependency>
	</dependencies>

使用数据层(mapper),服务层(service),表现层(controller)三层架构,结合对象(model),以及数据库进行演示:

model:

@Data
public class UserInfo {
    private Integer id;
    private String userName;
    private String password;
    private Date createTime;
    private Date updateTime;
}

@Data
public class LogInfo {
    private Integer id;
    private String userName;
    private String op;
    private Date createTime;
    private Date updateTime;
}

mapper:

@Mapper
public interface UserInfoMapper {
    @Insert("insert into user_info (user_name, password) values (#{userName}, #{password})")
    Integer insertUserInfo(String userName, String password);
}

@Mapper
public interface LogInfoMapper {
    @Insert("insert into log_info (user_name, op) values (#{userName}, #{op})")
    Integer insertLogInfo(String userName, String op);
}

service:

@Service
public class UserInfoService {
    @Autowired
    private UserInfoMapper userInfoMapper;

    // 演示代码时,会改变事务的传播机制
    @Transactional(propagation = Propagation.REQUIRED)
    public Integer insertUserInfo(String userName, String password) {
        return userInfoMapper.insertUserInfo(userName, password);
    }
}

@Service
public class LogInfoService {
    @Autowired
    private LogInfoMapper logInfoMapper;

    // 演示代码时,会改变事务的传播机制
    @Transactional(propagation = Propagation.REQUIRED)
    public Integer insertLogInfoService(String userName, String op) {
        return logInfoMapper.insertLogInfo(userName, op);
    }
}

controller:

@RestController
@RequestMapping("/prop")
public class PropagationController {
    @Autowired
    private UserInfoService userInfoService;
    @Autowired
    private LogInfoService logInfoService;

    @Transactional(propagation = Propagation.REQUIRED)
    @RequestMapping("/insert")
    public boolean insert(String userName, String password){
        // 用户注册
        userInfoService.insertUserInfo(userName, password);
        // 用户操作
        logInfoService.insertLogInfo(userName, "用户注册");

        return true;
    }
}

数据库表:user_info 和 log_info 初始为空

2.Propagation.REQUIRED

代码如下:

@Service
public class UserInfoService {
    @Autowired
    private UserInfoMapper userInfoMapper;

    @Transactional(propagation = Propagation.REQUIRED)
    public Integer insertUserInfo(String userName, String password) {
        return userInfoMapper.insertUserInfo(userName, password);
    }
}

@Service
public class LogInfoService {
    @Autowired
    private LogInfoMapper logInfoMapper;

    @Transactional(propagation = Propagation.REQUIRED)
    public Integer insertLogInfo(String userName, String op) {
        Integer result = 10 / 0;
        return logInfoMapper.insertLogInfo(userName, op);
    }
}

调用insertUserInfo(),插入用户数据;

调用insertLogInfo(),插入一条日志数据,抛出异常;

观察执行结果:

分析:

用户数据插入成功,日志数据插入时,抛出异常;

传播机制设置为Propagation.REQUIRED,所以insert()方法和insertUserInfo(),insertLogInfo()方法共用一个事务;

日志数据插入时抛出异常,导致事务出现回滚,故用户表和日志表无数据插入;

3.Propagation.REQUIRES_NEW

代码如下:

@Service
public class UserInfoService {
    @Autowired
    private UserInfoMapper userInfoMapper;

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public Integer insertUserInfo(String userName, String password) {
        return userInfoMapper.insertUserInfo(userName, password);
    }
}

@Service
public class LogInfoService {
    @Autowired
    private LogInfoMapper logInfoMapper;

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public Integer insertLogInfo(String userName, String op) {
        Integer result = 10 / 0;
        return logInfoMapper.insertLogInfo(userName, op);
    }
}

调用insertUserInfo(),插入用户数据;

调用insertLogInfo(),插入一条日志数据,抛出异常;

观察执行结果:

分析:

用户数据插入成功,日志数据插入时,抛出异常;

传播机制设置为Propagation.REQUIRES_NEW,所以insert()方法和insertUserInfo(),insertLogInfo()方法各有一个事务,互相不干扰;

用户数据插入成功,insertUserInfo()事务自动提交;

日志数据插入时抛出异常,导致insertLogInfo()方法事务出现回滚。

4.Propagation.NEVER

代码如下:

@Service
public class UserInfoService {
    @Autowired
    private UserInfoMapper userInfoMapper;

    @Transactional(propagation = Propagation.NEVER)
    public Integer insertUserInfo(String userName, String password) {
        return userInfoMapper.insertUserInfo(userName, password);
    }
}

@Service
public class LogInfoService {
    @Autowired
    private LogInfoMapper logInfoMapper;

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public Integer insertLogInfo(String userName, String op) {
        Integer result = 10 / 0;
        return logInfoMapper.insertLogInfo(userName, op);
    }
}

调用insertUserInfo(),插入用户数据,抛出异常;

观察执行结果:

分析:

用户数据插入时,抛出异常;

传播机制设置为Propagation.NEVER,所以检测到insert()方法存在事务时,会抛出异常;

用户数据插入时抛出异常,insertUserInfo()事务自动回滚;

Spring日志:

5.Propagation.NESTED

代码如下:

@Service
public class UserInfoService {
    @Autowired
    private UserInfoMapper userInfoMapper;

    @Transactional(propagation = Propagation.NESTED)
    public Integer insertUserInfo(String userName, String password) {
        return userInfoMapper.insertUserInfo(userName, password);
    }
}

@Service
public class LogInfoService {
    @Autowired
    private LogInfoMapper logInfoMapper;

    @Transactional(propagation = Propagation.NESTED)
    public Integer insertLogInfo(String userName, String op) {
        Integer result = 10 / 0;
        return logInfoMapper.insertLogInfo(userName, op);
    }
}

调用insertUserInfo(),插入用户数据;

调用insertLogInfo(),插入一条日志数据,抛出异常;

观察执行结果:

分析:

用户数据插入成功,日志数据插入时抛出异常;

传播机制设置为Propagation.NESTED,insertUserInfo()的事务和insertLogInfo()的事务都是insert()的嵌套事务;

日志数据插入时抛出异常,导致insert()的事务自动回滚,用户数据和日志数据都没插入成功。

6.Propagation.REQUIRED和Propagation.NESTED区别

代码如下:

@Service
public class UserInfoService {
    @Autowired
    private UserInfoMapper userInfoMapper;

    @Transactional(propagation = Propagation.NESTED)
    public Integer insertUserInfo(String userName, String password) {
        return userInfoMapper.insertUserInfo(userName, password);
    }
}

@Service
public class LogInfoService {
    @Autowired
    private LogInfoMapper logInfoMapper;

    @Transactional(propagation = Propagation.NESTED)
    public Integer insertLogInfo(String userName, String op) {
        try{
            Integer result = 10 / 0;
            return logInfoMapper.insertLogInfo(userName, op);
        }catch (Exception e){
            e.printStackTrace();
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return 0;
        }
    }
}

调用insertUserInfo(),插入用户数据;

调用insertLogInfo(),插入一条日志数据,并成功捕获异常;

观察执行结果:

分析:

用户数据插入成功,日志数据插入时,成功捕获异常,并手动回滚;

传播机制设置为Propagation.NESTED,insertUserInfo()的事务和insertLogInfo()的事务都是insert()的嵌套事务;

用户数据插入成功,insertUserInfo()的事务自动提交;

日志数据插入时成功捕获异常,并设置手动回滚,insertLogInfo()的事务自动回滚,日志数据插入失败;

结论:

Propagation.NESTED可以实现事务的部分回滚;

Propagation.REQUIRED只能整个事务回滚。


总结

本文介绍了Spring事务的传播机制。

相关文章:

  • 项目合同从专家到小白
  • Java final关键字规范说明
  • Linux学习笔记——设备驱动
  • ‌UniApp 安卓打包完整步骤(小白向)
  • 前端面试宝典---创建对象的配置
  • android 下提示 SQLITECIPHER driver not loaded
  • 链式触发器
  • 如何使用Tomcat
  • Day 9
  • Node 处理 request 的过程中,都会更新哪些 metadata 和 property
  • 餐饮厨房开源监控安全系统的智能革命
  • 小刚说C语言刷题——第20讲 循环之嵌套循环
  • 【面经】兼顾频繁插入/删除和查询访问 非阻塞网络I/O模型 connect的阻塞性 `unique_ptr`的使用场景和析构机制
  • 20年AB1解码java
  • 【PyTorch项目实战】卷积(Convolution ) + 反卷积(Deconvolution)
  • 文章记单词 | 第27篇(六级)
  • WePY 框架:小程序开发的“Vue式”利器!!!
  • gogs私服对应SSH 协议配置
  • 基于 OpenHarmony 5.0 的星闪轻量型设备应用开发——Ch3 设备驱动开发
  • python基础:位置互换