1.事务的传播行为简介
2.事务的传播行为类型
如下图,一共有7种类型,常用的就3种,下面这张图详细的描述了事务的传播行为,那么我们接下来我们将测试常用的传播行为它们混合调用之后的效果
3.传播行为测试
3.1 REQUIRED
测试准备
PoductService
@Transactional(propagation = Propagation.REQUIRED)
public Boolean required(Product product){
return save(product);
}
UserService
@Override
@Transactional(propagation = Propagation.REQUIRED)
public Boolean required(User user) {
return save(user);
}
@Override
@Transactional(propagation = Propagation.REQUIRED)
public Boolean requiredThrowing(User user) {
boolean save = save(user);
int a = 1/0;
return save;
}
外围方法没开启事务
测试方法
//测试1
public void requireTest1(){
productService.required(new Product().setProductName("大米"));
userService.required(new User().setUsername("张三").setPassword("123"));
}
//测试2
public void requireTest1(){
productService.required(new Product().setProductName("大米"));
userService.requiredThrowing(new User().setUsername("张三").setPassword("123"));
}
测试结果
测试1全插入成功
测试2:productService插入成功,userService插入失败
结果分析
- REQUIRED类型下,外围方法无事务,就自己开启事务,自己失败自己回滚,不影响其他方法
外围方法开启事务
测试方法
//测试1
@Transactional(propagation = Propagation.REQUIRED)
public void requireTest1(){
productService.required(new Product().setProductName("大米"));
userService.requiredThrowing(new User().setUsername("张三").setPassword("123"));
}
//测试2
@Transactional(propagation = Propagation.REQUIRED)
public void requireTest2(){
productService.required(new Product().setProductName("大米"));
try{
userService.requiredThrowing(new User().setUsername("张三").setPassword("123"));
}catch (Exception e){
e.printStackTrace();
}
}
//测试3
@Transactional(propagation = Propagation.REQUIRED)
public void requireTest3(){
productService.required(new Product().setProductName("大米"));
userService.required(new User().setUsername("张三").setPassword("123"));
int a = 1/0;
}
//测试4
@Transactional(propagation = Propagation.REQUIRED)
public void requireTest1(){
productService.required(new Product().setProductName("大米"));
userService.required(new User().setUsername("张三").setPassword("123"));
try {
int a = 1/0;
}catch (Exception e){
e.printStackTrace();
}
}
测试结果
测试1全回滚
测试2全回滚
测试3全回滚
测试4全部插入成功
结果分析
- 测试1:ProductService抛出异常,两个service均插入失败,这符合REQUIRED的特性,两个Service都是REQUIRED类型,外围方法也是REQUIRED类型,所以两个Service方法都加入外围方法,算一个事务,无论哪个方法抛出异常,整体都会回滚。
- 测试2:与测试4对应,虽然测试2捕捉了异常,但是userService.requiredThrowing并不是外围类的方法,所以异常会先抛给Spring,Spring再抛到外面来,由于异常经过了Spring,所以会触发回滚,结果与测试1一样,两个插入都失败。
- 测试3:这个没什么好说的,外面抛出异常,全部回滚。
- 测试4:与测试2对应,这个测试很典型,异常没有抛给Spring,而是在外围被捕捉了,所以出了异常也没有回滚,切记不要犯此低级错误。
3.2 REQUIRED_NEW
测试准备
productService
@Transactional(propagation = Propagation.REQUIRES_NEW)
public Boolean required(Product product){
return save(product);
}
userService
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW)
public Boolean required(User user) {
return save(user);
}
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW)
public Boolean requiredThrowing(User user) {
boolean save = save(user);
int a = 1/0;
return save;
}
外围方法没开事务
测试方法
//测试1
public void requireTest1(){
productService.required(new Product().setProductName("大米"));
userService.required(new User().setUsername("张三").setPassword("123"));
}
//测试2
public void requireTest1(){
productService.required(new Product().setProductName("大米"));
userService.requiredThrowing(new User().setUsername("张三").setPassword("123"));
}
测试结果
测试1全插入成功
测试2:productService插入成功,userService插入失败
结果分析
- 与REQUIRED一样,REQUIRED_NEW类型的外围方法无事务,就自己开启事务,自己失败自己回滚,不影响其他方法
外围方法开启事务
测试方法
//测试1
@Transactional
public void requireTest1(){
productService.required(new Product().setProductName("大米"));
userService.requiredThrowing(new User().setUsername("张三").setPassword("123"));
}
//测试2
@Transactional
public void requireTest3(){
productService.required(new Product().setProductName("大米"));
userService.required(new User().setUsername("张三").setPassword("123"));
int a = 1/0;
}
测试结果
测试1:ps插入成功,us插入失败
测试2:均插入成功
结果分析
- 测试1:REQUIRED_NEW类型会把外围事务挂起,自己开自己的事务,所以us抛出异常,是在自己的地盘上,跟外面的方法没关系。
- 测试2:这里时最能体现REQUIRED_NEW应用场景的地方,如:日志,外围方法出异常了,但是日志还是要插入的,插入日志使用REQUIRED_NEW最合适不过了
3.3 SUPPORTS
测试准备
productService
@Transactional(propagation = Propagation.SUPPORTS)
public Boolean required(Product product){
return save(product);
}
userService
@Override
@Transactional(propagation = Propagation.SUPPORTS)
public Boolean required(User user) {
return save(user);
}
@Override
@Transactional(propagation = Propagation.SUPPORTS)
public Boolean requiredThrowing(User user) {
boolean save = save(user);
int a = 1/0;
return save;
}
外围方法没开事务
测试方法
//测试1
@Transactional
public void requireTest1(){
productService.required(new Product().setProductName("大米"));
userService.requiredThrowing(new User().setUsername("张三").setPassword("123"));
}
//测试2
@Transactional(propagation = Propagation.SUPPORTS)
public void requireTest3(){
productService.required(new Product().setProductName("大米"));
userService.requiredThrowing(new User().setUsername("张三").setPassword("123"));
}
测试结果
测试1全插入成功
测试2全插入成功
结果分析
- 由于SUPPORTS类型本身是不会自己开启事务的,它需要融入外围方法的事务,外围方法的REQUIRED与REQUIRED_NEW以及NESTED类型,只有这三种类型才会开启事务,其他4种类型是不会开启事务的,所以上面的测试1、2相当于没有事务在运行。
外围方法开启事务
测试方法
//测试1
@Transactional
public void requireTest1(){
productService.required(new Product().setProductName("大米"));
userService.requiredThrowing(new User().setUsername("张三").setPassword("123"));
}
//测试2
@Transactional
public void requireTest3(){
productService.required(new Product().setProductName("大米"));
userService.required(new User().setUsername("张三").setPassword("123"));
int a = 1/0;
}
测试结果
测试1全插入失败
测试2全插入失败
测试分析
- 由于SUPPORTS需要融入外部事务,自己不会开启事务,所以上述测试1、2可以看成一个事务,出异常就会全部回滚。
总结
- 如果外围方法需要事务,那么传播行为只能是REQUIRED与REQUIRED_NEW以及NESTED类型,只要这三会开事务,其他不会开事务。
- 只要按着上面那张图来配置事务的传播行为,那么基本不会出现什么大问题。