大家好,Spring事务传播机制大家应该都知道,不管是平时的工作还是备战面试,都会被问到,很多时候我们都跟背八股文一样背这个规律,但是却没有实践过,今天我打算用一个实践的操作来跟大家一起温习一下这些事务传播机制。
首先我们知道事务传播机制分为以下几种
事务传播级别 | 含义 | 外层有事务 | 外层无事务 | 备注 |
REQUIRED | 默认事务级别 | 加入外层事务 | 新建一个事务 | |
SUPPORTS | 支持当前事务 | 加入外层事务 | 以非事务方式运行 | |
MANDATORY | 强制事务执行 | 加入外层事务 | 报错 | |
REQUIRES_NEW | 新建一个新事务 | 挂起并新建一个事务 | 新建一个事务 | 新老事务相互独立 |
NOT_SUPPORTED | 以非事务方式运行 | 挂起外层事务 | 以非事务方式运行 | |
NEVER | 以非事务方式运行 | 报错 | 以非事务方式运行 | |
NESTED | 嵌套事务 | 加入外层事务 | 新建一个事务 | 外层回滚,内层也要回滚, 内层回滚,外层不用回滚。 |
接着我以一些实践操作来证实上面的结论。
1、REQUIRED
① 外层没有事务,则新建一个事务
外层代码:
/**
* 默认事务,外层没有事务,开启一个新的事务
* 数据正常保存到数据库
*/
@Test
public void save() {
requiredService.save("chenhuaijie");
}
内层代码:
/**
* 默认配置事务配置
*
* @param username
*/
@Transactional
public void save(String username) {
User user = User.builder().username(username).build();
userRepository.save(user);
}
结果:
② 外层有事务,则加入外层事务
外层代码:
/**
* 外层事务
*
* @param username
*/
@Transactional
public void save(String username) {
requiredService.save(username);
}
内层代码:
/**
* 默认配置事务配置
*
* @param username
*/
@Transactional
public void save(String username) {
User user = User.builder().username(username).build();
userRepository.save(user);
}
结果
2、SUPPORTS
① 外层无事务,以非事务方式运行
外层代码
/**
* 非事务方式运行
* 正常保存数据
*
* @param username
*/
public void save(String username) {
supportsService.save(username);
}
内层代码
/**
* Support事务配置
*
* @param username
*/
@Transactional(propagation = Propagation.SUPPORTS)
public void save(String username) {
User user = User.builder().username(username).build();
userRepository.save(user);
}
结果,在service层并没有创建事务
② 外层有事务,加入外层事务
外层代码
/**
* 以事务方式运行
* 正常保存数据
*
* @param username
*/
@Transactional
public void saveWithTransaction(String username) {
supportsService.save(username);
}
内层代码
/**
* Support事务配置
*
* @param username
*/
@Transactional(propagation = Propagation.SUPPORTS)
public void save(String username) {
User user = User.builder().username(username).build();
userRepository.save(user);
}
结果
3、MANDATORY
①外层有事务,加入外层事务
外层代码
/**
* 加入当前事务
* 数据保存正常
*
* @param username
*/
@Transactional
public void saveWithTransaction(String username) {
mandatoryService.save(username);
}
内层代码
/**
* 外层有事务就跟着外层事务,外层没有事务就报错
*
* @param username
*/
@Transactional(propagation = Propagation.MANDATORY)
public void save(String username) {
User user = User.builder().username(username).build();
userRepository.save(user);
}
结果
②外层无事务,报错
外层代码
/**
* 报错
*
* @param username
*/
public void save(String username) {
mandatoryService.save(username);
}
内层代码
/**
* 外层有事务就跟着外层事务,外层没有事务就报错
*
* @param username
*/
@Transactional(propagation = Propagation.MANDATORY)
public void save(String username) {
User user = User.builder().username(username).build();
userRepository.save(user);
}
结果
4、REQUIRES_NEW
①外层有事务,挂起外层事务并新建一个事务
外层代码
/**
* 存在事务,则新启动一个新的事务
*
* @param username
*/
@Transactional
public void saveWithTransaction(String username) {
requiredNewService.save(username);
}
内层代码
/**
* 要求启动一个新的事务
*
* @param username
*/
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void save(String username) {
User user = User.builder().username(username).build();
userRepository.save(user);
}
结果
②外层无事务,新建一个事务
外层代码
/**
* 没有事务,以事务方式运行
*
* @param username
*/
public void save(String username) {
requiredNewService.save(username);
}
内层代码
/**
* 要求启动一个新的事务
*
* @param username
*/
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void save(String username) {
User user = User.builder().username(username).build();
userRepository.save(user);
}
结果
5、NOT_SUPPORTED
①:外层有事务,挂起外层事务
外层代码
@Transactional
public void saveWithTransaction(String username) {
notSupportedService.save(username);
}
内层代码
/**
* 如果当前事务存在则挂起当前事务
*
* @param username
*/
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void save(String username) {
User user = User.builder().username(username).build();
userRepository.save(user);
}
结果
②:外层无事务,以非事务方式运行
外层代码
public void save(String username) {
notSupportedService.save("chenhuaijie");
}
内层代码
/**
* 如果当前事务存在则挂起当前事务
*
* @param username
*/
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void save(String username) {
User user = User.builder().username(username).build();
userRepository.save(user);
}
结果
6、NEVER
①:外层有事务,报错
外层代码
/**
* 报错
*
* @param username
*/
@Transactional
public void saveWithTransaction(String username) {
neverService.save(username);
}
内层代码
/**
* 外层有事务就跟着外层事务,外层没有事务就报错
*
* @param username
*/
@Transactional(propagation = Propagation.NEVER)
public void save(String username) {
User user = User.builder().username(username).build();
userRepository.save(user);
}
结果
②:外层无事务,以非事务方式运行
外层代码
/**
* 非事务方式运行
* 数据保存正常
*
* @param username
*/
public void save(String username) {
neverService.save(username);
}
内层代码
/**
* 外层有事务就跟着外层事务,外层没有事务就报错
*
* @param username
*/
@Transactional(propagation = Propagation.NEVER)
public void save(String username) {
User user = User.builder().username(username).build();
userRepository.save(user);
}
结果
7、NESTED
jpa不支持nested事务传播机制,nested的特点是,
① 外层有事务,加入外层事务,类似于REQUIRED
外层代码
/**
* 如果外层有事务,则内层新增一个嵌套事务
*/
@Transactional(transactionManager = "dataSourceTransactionManager")
public void saveWithTransaction(String username) {
nestedService.save(username);
}
内层代码
/**
* 如果当前存在事务则执行一个嵌套事务,如果当前没有事务则新建一个事务
* 如果外层事务回滚了,内层事务要跟着回滚;如果内层事务回滚了,外层事务不需要回滚
*
* @param username
*/
@Transactional(transactionManager = "dataSourceTransactionManager", propagation = Propagation.NESTED)
public void save(String username) {
User user = User.builder().username(username).build();
userTemplate.save(user);
}
结果
② 外层无事务,新建一个事务,类似于REQUIRED_NEW
外层代码
/**
* 外层没有事务,则内层新建一个事务,此时类似于REQUIRED_NEW
*/
public void save(String username) {
nestedService.save(username);
}
内层代码
/**
* 如果当前存在事务则执行一个嵌套事务,如果当前没有事务则新建一个事务
* 如果外层事务回滚了,内层事务要跟着回滚;如果内层事务回滚了,外层事务不需要回滚
*
* @param username
*/
@Transactional(transactionManager = "dataSourceTransactionManager", propagation = Propagation.NESTED)
public void save(String username) {
User user = User.builder().username(username).build();
userTemplate.save(user);
}
结果
外层回滚带动内层回滚:这种看起来是一个事务的效果
内层回滚不影响外层事务提:这种看起来是独立事务的效果
在一个注解上可以实现两种效果,这也是嵌套事务的巧妙之处。
外层代码:
/**
* 实现如下场景:
* 外事务回滚,内事务也要回滚
* 但是如果内事务回滚了,外事务不用回滚
*/
@Transactional(transactionManager = "dataSourceTransactionManager", rollbackFor = NullPointerException.class)
public void save(boolean rollbackOuter, boolean rollbackInner) {
userTemplate.save(User.builder().username("1").build());
try {
nestedService.saveWithNullPointException("2", rollbackInner);
} catch (Exception e) {
}
if (rollbackOuter) {
throw new NullPointerException();
}
}
内层代码:
@Transactional(transactionManager = "dataSourceTransactionManager", propagation = Propagation.REQUIRES_NEW)
public void saveWithNullPointException(String username, boolean rollback) {
User user = User.builder().username(username).build();
userTemplate.save(user);
if (rollback) {
int i = 10 / 0;
}
}
测试代码:
// ====================================================================================================
// 只有将传播机制设置为NESTED级别,才会同时实现如下两种场景的要求,改成REQUIRED或者REQUIRES_NEW都只能满足其中一种
/**
* 内事务回滚,外事务不回滚
*/
@Test
public void save1() {
nestedOuterService.save(false, true);
}
/**
* 外事务回滚,内事务一起回滚
*/
@Test
public void save2() {
nestedOuterService.save(true, false);
}
// ====================================================================================================