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事务的传播机制。