桃树李树虽不会说话,但是它们果实甜美,惹人喜爱,人们在它下面走来走去,走成了一条小路。
spring事务传播机制
前言
spring事务传播行为的含义:
简单的理解就是多个事务方法相互调用时,事务如何在这些方法间传播。
默认是REQUIRED。
一、7种事务传播类型
7种事务传播类型分别是:
REQUIRED、SUPPORTS、MANDATORY
REQUIRES_NEW、NOT_SUPPORTED、NEVER
NESTED
这种东西没必要刻意去记,死记硬背过后也是会遗忘的,我们要真正理解其含义。
spring事务传播机制可以分为三组:
1.1 支持当前事务
支持当前事务的传播机制有三种,分别是
1 REQUIRED (必须有)
含义:如果当前方法没有事务,新建一个事务,如果已经存在一个事务中,则加入到这个事务中。
2 SUPPORTS (可有可无)
含义:支持当前事务,如果当前没有事务,就以非事务方式执行
3 MANDATORY (强制)
含义:使用当前的事务,如果当前没有事务,就抛出异常。
1.2 不支持当前事务
4 REQUIRES_NEW
含义:新建事务,如果当前存在事务,把当前事务挂起。
5 NOT_SUPPORTED
含义:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
6 NEVER
含义: 以非事务方式执行,如果当前存在事务,则抛出异常。
1.3 NESTED
含义: 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。
二、示例
案例场景:每次调用创建用户接口的同时,都要记录一下该操作日志;创建用户方法会向user表中插入一条数据,操作日志方法会向log表中插入一条数据。
2.1 saveUser 配置事务 required, saveLog 无事务
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserRepository userRepository;
@Autowired
private LogService logService;
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void saveUser() {
User user = new User();
user.setName("dly");
userRepository.save(user);
saveLog();
}
@Override
public void saveLog() {
Log log = new Log();
log.setContent("新增用户");
logService.saveLog(log);
// 异常 / by zero
int i = 1 / 0;
}
}
结论:在这个案例中user、log都不会存储成功。
原因: 1. addUser 方法中配置事务为required
2. saveLog未配置事务,但是addUser调用了saveLog,由于事务传播类型为requried,所以saveLog也是有事务的, 所以出现异常时,触发回滚,哪个也存不进去。
2.2 saveUser 无事务, saveLog事务为required
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserRepository userRepository;
@Autowired
private LogService logService;
@Autowired
private UserService userService;
@Override
public void saveUser() {
User user = new User();
user.setName("dly");
userRepository.save(user);
userService.saveLog();
}
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void saveLog() {
Log log = new Log();
log.setContent("新增用户");
logService.saveLog(log);
// 异常
int i = 1 / 0;
}
结论:user插入成功,log失败
原因:1.saveUser方法没有事务
2.saveUser调用了saveLog,saveLog方法的事务只作用在当前方法上面,因此发生异常时,log回滚,user没有事务,不回滚。
补充:因为spring 中事务的实现使用的是AOP,也就是通过动态代理实现的,因此,在案例中需要注入Service类型,借助通过spring代理的service对象来完成同一个类中方法的相互调用,不能使用this.方法名(),直接this调用是没有事务的。
2.3 saveUser 配置事务required,saveLog配置事务not_supported
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserRepository userRepository;
@Autowired
private LogService logService;
@Autowired
private UserService userService;
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void saveUser() {
User user = new User();
user.setName("dly");
userRepository.save(user);
userService.saveLog();
}
@Override
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void saveLog() {
Log log = new Log();
log.setContent("新增用户");
logService.saveLog(log);
// 异常
int i = 1 / 0;
}
结论:user 插入失败,log插入成功
原因:1.saveUser 事务是required
2.saveLog事务是not_supported,意思是以非事务方式运行,如果存在事务,则把事务挂起,因此可以理解为saveLog方法是没有事务的,所以抛处异常后,user回滚,log不回滚。
not_supported,以非事务方式运行在记录日志这种场景时是很合适的,就日志而言,不管你成功与否,我都要记录。
2.4 saveUser事务required,saveLog配置事务never
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserRepository userRepository;
@Autowired
private LogService logService;
@Autowired
private UserService userService;
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void saveUser() {
User user = new User();
user.setName("dly");
userRepository.save(user);
userService.saveLog();
}
@Override
@Transactional(propagation = Propagation.NEVER)
public void saveLog() {
Log log = new Log();
log.setContent("新增用户");
logService.saveLog(log);
}
结论:user、log 都插入失败
原因:1.saveLog中配置的事务机制为never,表示如果存在事务,则抛处异常,因此user、log全部回滚。
抛出的异常为:
IllegalTransactionStateException: Existing transaction found for transaction marked with propagation ‘never’
总结
spring的事务传播默认使用的是required, 也是最常用的,而且spring的事务处理是使用aop动态代理来实现的,如果使用不当就会出现spring事务失效的问题。