事务传播机制
- 一. 事务传播机制是什么
- 二. 为什么需要事务传播机制
- 三. 事务传播机制有哪些
- 四. Spring 事务传播机制演示
- 1. ⽀持当前事务(REQUIRED)
- 2. 不支持当前事务(REQUIRES_NEW)
- 3. 不⽀持当前事务,NEVER 抛异常
- 4. NESTED 嵌套事务
一. 事务传播机制是什么
Spring 事务传播机制定义了多个包含了事务的⽅法,相互调⽤时,事务是如何在这些⽅法间进⾏传递的。
二. 为什么需要事务传播机制
事务隔离级别是保证多个并发事务执⾏的可控性的(稳定性的),⽽事务传播机制是保证⼀个事务在多个调⽤⽅法间的可控性的(稳定性的)。
举个栗子:
比如新冠病毒,它有不同的隔离⽅式(酒店隔离还是居家隔离),是为了保证疫情可控,然⽽在每个⼈的隔离过程中,会有很多个执⾏的环节,⽐如酒店隔离,需要负责⼈员运送、物品运送、消杀原⽣活区域、定时核算检查和定时送餐等很多环节,⽽事务传播机制就是保证⼀个事务在传递过程中是可靠性的,回到本身案例中就是保证每个⼈在隔离的过程中可控的。
事务隔离级别解决的是多个事务同时调⽤⼀个数据库的问题,如下图:
⽽事务传播机制解决的是⼀个事务在多个节点(⽅法)中传递的问题,如下图所示:
方法 1 调用 方法 2, 方法 2 调用方法 3:
三. 事务传播机制有哪些
Spring 事务传播机制包含以下 7 种:
- Propagation.REQUIRED:默认的事务传播级别,它表示如果当前存在事务,则加⼊该事务;如果当前没有事务,则创建⼀个新的事务。
- Propagation.SUPPORTS:如果当前存在事务,则加⼊该事务;如果当前没有事务,则以⾮事务的⽅式继续运⾏。
- Propagation.MANDATORY:(mandatory:强制性)如果当前存在事务,则加⼊该事务;如果当前没有事务,则抛出异常。
- Propagation.REQUIRES_NEW:表示创建⼀个新的事务,如果当前存在事务,则把当前事务挂起。也就是说不管外部⽅法是否开启事务,Propagation.REQUIRES_NEW 修饰的内部⽅法会新开启⾃⼰的事务,且开启的事务相互独⽴,互不⼲扰。
- Propagation.NOT_SUPPORTED:以⾮事务⽅式运⾏,如果当前存在事务,则把当前事务挂起。
- Propagation.NEVER:以⾮事务⽅式运⾏,如果当前存在事务,则抛出异常。
- Propagation.NESTED:如果当前存在事务,则创建⼀个事务作为当前事务的嵌套事务来运⾏;如果当前没有事务,则该取值等价于 PROPAGATION_REQUIRED。
以上 7 种传播⾏为,可以根据是否⽀持当前事务分为以下 3 类:
以情侣关系为例来理解以上分类:
四. Spring 事务传播机制演示
1. ⽀持当前事务(REQUIRED)
先开启事务先成功插⼊⼀条⽤户数据,然后再执⾏⽇志报错,⽽在⽇志报错是发⽣了异常,观察 propagation = Propagation.REQUIRED 的执⾏结果。
controller 层,传播机制为 REQUIRED,先调用保存用户,再调用保存 日志
@RestController
public class UserController {
@Resource
private UserService userService;
@Resource
private LogService logService;
@RequestMapping("/save")
@Transactional(propagation = Propagation.REQUIRED)
public Object save(User user) {
// 插⼊⽤户操作
userService.save(user);
// 插⼊⽇志
logService.saveLog("⽤户插⼊:" + user.getUsername());
return true;
}
}
UserService ,传播机制为 REQUIRED,正常保存
@Service
public class UserService {
@Resource
private UserMapper userMapper;
@Transactional(propagation = Propagation.REQUIRED)
public int save(User user) {
System.out.println("执⾏ save ⽅法.");
return userMapper.save(user);
}
}
LogService ,传播机制为 REQUIRED,抛异常,注意异常一定要捕获
@Service
public class LogService {
@Resource
private LogMapper logMapper;
@Transactional(propagation = Propagation.REQUIRED)
public int saveLog(String content) {
int result = logMapper.saveLog(content);
// 出现异常
try {
int i = 10 / 0;
} catch (Exception e) {
// 一定要捕捉异常
// 然后手动回滚
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
return result;
}
}
执⾏结果:用户表和日志表都没有插⼊任何数据。
- UserService 中的保存⽅法正常执⾏完成。
- LogService 保存⽇志程序报错。
- 因为三个方法的事务传播机制使⽤的都是 REQURIED, UserService、 LogService 和 Controller 使用同一个事务,所以整个事务回滚。数据库中没有插⼊任何数据,也就是步骤 1 中的⽤户插⼊⽅法也回滚了。
2. 不支持当前事务(REQUIRES_NEW)
UserController 类中的代码不变,将添加⽇志的⽅法修改为 REQUIRES_NEW 不⽀持当前事务,重新创建事务,观察执⾏结果:
controller 层,传播机制为 REQUIRED,先调用保存用户,再调用保存 日志
@RestController
public class UserController {
@Resource
private UserService userService;
@Resource
private LogService logService;
@RequestMapping("/save")
@Transactional(propagation = Propagation.REQUIRED)
public Object save(User user) {
// 插⼊⽤户操作
userService.save(user);
// 插⼊⽇志
logService.saveLog("⽤户插⼊:" + user.getUsername());
return true;
}
}
UserService ,传播机制为 REQUIRES_NEW,正常保存
@Service
public class UserService {
@Resource
private UserMapper userMapper;
@Transactional(propagation = Propagation.REQUIRES_NEW)
public int save(User user) {
System.out.println("执⾏ save ⽅法.");
return userMapper.save(user);
}
}
LogService ,传播机制为 REQUIRES_NEW,抛异常, 注意异常一定要捕获
@Service
public class LogService {
@Resource
private LogMapper logMapper;
@Transactional(propagation = Propagation.REQUIRES_NEW)
public int saveLog(String content) {
int result = logMapper.saveLog(content);
// 出现异常
try {
int i = 10 / 0;
} catch (Exception e) {
// 一定要捕捉异常
// 然后手动回滚
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
return result;
}
}
程序执⾏结果:User 表中成功插⼊了数据,Log 表执⾏失败,但没影响 UserController 中的事务。
3. 不⽀持当前事务,NEVER 抛异常
controller 层,传播机制为 REQUIRED,只调用保存用户
@RestController
public class UserController {
@Resource
private UserService userService;
@RequestMapping("/save")
@Transactional(propagation = Propagation.REQUIRED)
public Object save(User user) {
// 插⼊⽤户操作
userService.save(user);
return true;
}
}
UserService ,传播机制为 NEVER,正常保存
@Service
public class UserService {
@Resource
private UserMapper userMapper;
@Transactional(propagation = Propagation.NEVER)
public int save(User user) {
System.out.println("执⾏ save ⽅法.");
return userMapper.save(user);
}
}
程序执⾏报错,⽤户表未添加任何数据。
4. NESTED 嵌套事务
controller 层,传播机制为 REQUIRED/NESTED/或者不写参数都行,调用保存用户
@RestController
public class UserController {
@Resource
private UserService userService;
@RequestMapping("/save")
@Transactional(propagation = Propagation.REQUIRED)
public Object save(User user) {
// 插⼊⽤户操作
userService.save(user);
return true;
}
}
UserService ,传播机制为 NESTED ,正常保存
@Service
public class UserService {
@Resource
private UserMapper userMapper;
@Resource
private LogService logService;
@Transactional(propagation = Propagation.NESTED)
public int save(User user) {
int result = userMapper.save(user);
System.out.println("执⾏ save ⽅法.");
// 调用插入插⼊⽇志的方法
logService.saveLog("⽤户插⼊:" + user.getUsername());
return result;
}
}
LogService ,传播机制为 NESTED ,抛异常, 注意异常一定要捕获
@Service
public class LogService {
@Resource
private LogMapper logMapper;
@Transactional(propagation = Propagation.NESTED)
public int saveLog(String content) {
int result = logMapper.saveLog(content);
// 出现异常
try {
int i = 10 / 0;
} catch (Exception e) {
// 一定要捕捉异常
// 然后手动回滚
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
return result;
}
}
最终程序的执⾏结果:
⽤户表中数据添加成功,⽇志表中没有添加任何数据,Log 中的事务已经回滚,但是嵌套事务不会回滚嵌套之前的事务,也就是说嵌套事务可以实现部分事务回滚。
嵌套事务之所以能够实现部分事务的回滚,是因为事务中有⼀个保存点(savepoint)的概念,嵌套事务进⼊之后相当于新建了⼀个保存点,⽽回滚时只回滚到当前保存点,因此之前的事务是不受影响的。
嵌套事务(NESTED)和加⼊事务(REQUIRED )的区别:
- 整个事务如果全部执⾏成功,⼆者的结果是⼀样的。
- 如果事务执⾏到⼀半失败了,那么加⼊事务整个事务会全部回滚;⽽嵌套事务会局部回滚,不会影响上⼀个⽅法中执⾏的结果。
嵌套事务(NESTED)和新建事务(REQUIRE_NEW )的区别:
- 对于 NESTED,当外部事务回滚时,内部事务一定回滚。
- 对于 REQUIRE_NEW,外部事务与内部事务之间是相互独立的,所以外部事务回滚时,内部事务不会回滚。
注意:
- 当一个事务中出现异常时一定要将它 catch 然后手动回滚事务,如果不 catch 那么异常就会沿着调用链一直往上抛,这样只要是涉及到这个异常的所有事务都会回滚。
- 只有 NESTED 嵌套事务是类似于能同时存在多个事务,其他六种传播机制都是同一时刻只存在一个事务。
- 对于 NESTED,外部事务提交之前,内部事务无法提交,即外部事务回滚,内部事务一定回滚。
好啦! 以上就是对 Spring 事务传播机制 的讲解,希望能帮到你 !
评论区欢迎指正 !