定义了多个包含了事务的⽅法,相互调⽤时,事务是如何在这些⽅法间进⾏传递的
事务隔离级别是保证多个并发事务执⾏的可控性的(稳定性的),而事务传播机制是保证⼀个事务在多个调用方法间的可控性的(稳定性的)
上面的图片很好的解释了事务传播机制和事务隔离级别的流程
Spring事务传播机制定义了在多个事务方法相互调用时,不同事务方法间事务处理的行为方式。Spring框架提供了7种不同的事务传播行为方式,分别是:
3 MANDATORY:当前方法必须在事务中执行,存在事务,就会加入该事务,如果不存在事务,则抛出异常。
4.REQUIRES_NEW:表示创建⼀个新的事务,如果当前存在事务,则把当前事务挂起。也就是说不管外部⽅法是否开启事务,Propagation.REQUIRES_NEW 修饰的内部⽅法会新开启⾃⼰的事务,且开启的事务相互独⽴,互不⼲扰。
5.NOT_SUPPORTED:以⾮事务⽅式运⾏,如果当前存在事务,则把当前事务挂起
6.NEVER:当前方法不能在事务中执行,如果存在事务,则抛出异常。
可以清楚的看到没有插入
控制台连受影响的行数都没有打印,直接抛出异常
这个NESTED比较绕,我们一起努力,冲冲冲!
7.NESTED:创建一个嵌套事务,如果当前存在事务,则将该事务作为嵌套事务的父事务,如果父事务提交,则嵌套事务会提交,否则回滚。
如果当前没有事务,则该取值等价于 PROPAGATION_REQUIRED。
执行流程
当没有事务,创建事务,后续方法会生成嵌套事务,并且有一个保存点,一旦该事务出现问题,就会回滚到上一个事务保存点,不会影响其他事务的执行
package com.example.demo.controller;
import com.example.demo.model.Userinfo;
import com.example.demo.service.UserService;
import org.apache.catalina.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.util.pattern.PathPattern;
//测试类
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@RequestMapping("/add")
@Transactional(propagation = Propagation.REQUIRED)
public int add(){
Userinfo userinfo=new Userinfo();
userinfo.setUsername("老七");
userinfo.setPassword("123");
int result= userService.add(userinfo);
System.out.println("受影响的行数是"+result);
userService.insert(userinfo);
return result;
}
}
package com.example.demo.service;
import com.example.demo.mapper.UserMapper;
import com.example.demo.model.Userinfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
@Transactional(propagation = Propagation.NESTED)
public int add(Userinfo userinfo){
int result=userMapper.add(userinfo);
System.out.println("受影响的行数"+result);
return result;
}
@Transactional(propagation = Propagation.NESTED)
public int insert(Userinfo userinfo){
int result=userMapper.add(userinfo);
System.out.println("受影响的行数"+result);
int num=10/0;
return result;
}
}
数据库是啥也没插入的,为啥呢?不是说会回滚到上一个事务保存点吗?所以应该插入一条,为啥一条也没有
因为:
第三个出了问题,那么创建事务的作为调用方,一定可以感知到异常,那么整个事务就会回滚,所以一条都不会插入
所以某个方法报异常不要让总的调用者感知到,就不会全部回滚,那么可以采用如下方式
修改这里的代码,让该方法感知到异常即可,不让总方法感知到,那么就会执行回滚操作,回滚到上一个事务保存点,所以只会插入一条数据
在写这个例子的时候,我们特意修改,不在add方法里面调用insert方法,而是在userController中调用,为什么呢?
因为NESTED嵌套NESTED的时候,上一个NESTED保存点设置不上,这两个保存点会进行合并,合并成一个临时任务,那么一个挂掉,两个都挂了,所以在总的调用方法中调用它,才能有效果
再举一个例子:
公司雇了一个清洁工A,清洁工自己有又雇了一个清洁工B,当清洁B干了坏事,那么公司就会把A和B同时开除,当公司雇了两个清洁工,其中一个清洁工出了问题,就开掉出问题的那个就好,没出问题的留下~
这个例子就对应了代码的执行过程~