视频出处:https://www.bilibili.com/video/BV1te4y1J7Qd/?vd_source=44d1426ea434f76dfe83b4aa384fb2fa
1、什么是事务传播机制
事务的传播,是A方法调用B方法并将事务传递给它。事务的转播机制主要针对被调用者而言,控制它是否被传播或者被怎样传播。spring事务的传播机制有七种:
传播行为 | 描述 |
---|---|
PROPAGATION_REQUIRED | 默认的Spring事物传播级别,若当前存在事务,则加入该事务,若不存在事务,则新建一个事务 |
PROPAGATION_REQUIRE_NEW | 若当前没有事务,则新建一个事务。若当前存在事务,则新建一个事务,新老事务相互独立。外部事务抛出异常回滚不会影响内部事务的正常提交 |
PROPAGATION_NESTED | 如果当前存在事务,则嵌套在当前事务中执行。如果当前没有事务, 则新建一个事务,类似于REQUIRE_NEW |
PROPAGATION_SUPPORTS | 支持当前事务,若当前不存在事务,以非事务的方式执行 |
PROPAGATION_NOT_SUPPORTED | 以非事务的方式执行,若当前存在事务,则把当前事务挂起 |
PROPAGATION_MANDATORY | 强制事务执行,若当前不存在事务,则抛出异常 |
PROPAGATION_NEVER | 以非事务的方式执行,如果当前存在事务,则抛出异常 |
传播级别一般不需要定义,默认就是PROPAGATION_REQUIRED,除非在嵌套事务的情况。上述描述表格的描述还是比较抽象,下面我们使用一个例子来说明这个传播机制。假定方法A调用方法B:
方法B定义的事务类型 | A方法有事务时 | A方法无事务 |
---|---|---|
@Transactional(propagation = Propagation.REQUIRED) | B和A事务合并成一个事务 | B新建一个事务 |
@Transactional(propagation = Propagation.REQUIRES_NEW) | B新建一个事务,和A事务无关,互不影响(A回滚不会影响到B的提交) | B新建一个事务 |
@Transactional(propagation = Propagation.NESTED) | B新建一个A的子事务,A回滚会导致B已经提交的事务一起回滚;B回滚不会影响到A | B新建一个事务 |
@Transactional(propagation = Propagation.SUPPORTS) | B加入到A事务中 | B无事务 |
@Transactional(propagation = Propagation.NOT_SUPPORTED) | 挂起A事务,B以无事务方式执行 | B无事务 |
@Transactional(propagation = Propagation.MANDATORY) | B加入到A事务中 | B抛异常 |
@Transactional(propagation = Propagation.NEVER) | B抛异常 | B无事务 |
2、七种事务传播机制
现在在这里,我们假定方法A
是 StudentService
类中的方法,方法B
是CourseService
类中的方法
2.1、@Transactional(propagation = Propagation.REQUIRED)
A中没有事务,B中有事务
@Service
public class StudentService {
@Autowired
private StudentMapper studentMapper;
@Autowired
private CourseService courseService;
//这里没有开启事务,courseService.insertCourse会自己开启一个事务运行
//而且两个添加操作都会成功,因为studentMapper.insert(student);是自动提交的
public void insert0(){
courseService.insertCourse();
Student student = new Student();
student.setId(1);
student.setName("student_"+ System.currentTimeMillis());
studentMapper.insert(student);
int i = 1/0;//这里抛出异常,两个添加操作都不会回滚
}
}
@Service
public class CourseService {
@Autowired
private CourseMapper courseMapper;
@Transactional(propagation = Propagation.REQUIRED)
public void insertCourse(){
Course course = new Course();
course.setId(1);
course.setName("course_"+ System.currentTimeMillis());
courseMapper.insert(course);
}
}
A和B两个方法都有事务
@Service
public class StudentService {
@Autowired
private StudentMapper studentMapper;
@Autowired
private CourseService courseService;
//两个方法都使用了@Transactional(propagation = Propagation.REQUIRED)
//courseService.insertCourse()会加入到insert0方法开启的事务中,所以两个方法在同一事务中
@Transactional(propagation = Propagation.REQUIRED)
public void insert0(){
courseService.insertCourse();
Student student = new Student();
student.setId(1);
student.setName("student_"+ System.currentTimeMillis());
studentMapper.insert(student);
int i = 1/0;//这里抛出异常,两个添加操作都被回滚
}
}
@Service
public class CourseService {
@Autowired
private CourseMapper courseMapper;
@Transactional(propagation = Propagation.REQUIRED)
public void insertCourse(){
Course course = new Course();
course.setId(1);
course.setName("course_"+ System.currentTimeMillis());
courseMapper.insert(course);
}
}
2.2、@Transactional(propagation = Propagation.REQUIRES_NEW)
在A方法的事务中抛出异常,B方法正确执行,正常提交
@Service
public class StudentService {
@Autowired
private StudentMapper studentMapper;
@Autowired
private CourseService courseService;
//courseService.insertCourse()方法用了@Transactional(propagation = Propagation.REQUIRES_NEW)
//开启了一个独立的新的事务,在独立的事务中运行,
// 并且insert0()开启的事务被挂起
//等courseService.insertCourse()运行完毕,再恢复insert0()的事务
//两个事务的提交和回滚都在各自事务中完成
@Transactional(propagation = Propagation.REQUIRED)
public void insert0(){
courseService.insertCourse();
Student student = new Student();
student.setId(1);
student.setName("student_"+ System.currentTimeMillis());
studentMapper.insert(student);
//这里抛出异常,courseService.insertCourse()不会回滚
//但是studentMapper.insert会被回滚
int i = 1/0;
}
}
@Service
public class CourseService {
@Autowired
private CourseMapper courseMapper;
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void insertCourse(){
Course course = new Course();
course.setId(1);
course.setName("course_"+ System.currentTimeMillis());
courseMapper.insert(course);
}
}
在B方法中抛出异常,A和B都被回滚
@Service
public class StudentService {
@Autowired
private StudentMapper studentMapper;
@Autowired
private CourseService courseService;
//courseService.insertCourse()方法用了@Transactional(propagation = Propagation.REQUIRES_NEW)
//courseService.insertCourse()会抛出异常给insert0()方法,导致两个方法都被回滚
@Transactional(propagation = Propagation.REQUIRED)
public void insert0(){
Student student = new Student();
student.setId(1);
student.setName("student_"+ System.currentTimeMillis());
studentMapper.insert(student);
//这里调用的insertCourse会抛出异常
courseService.insertCourse();
}
}
@Service
public class CourseService {
@Autowired
private CourseMapper courseMapper;
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void insertCourse(){
Course course = new Course();
course.setId(1);
course.setName("course_"+ System.currentTimeMillis());
courseMapper.insert(course);
//这里抛出异常,异常会被抛给调用这个方法的调用者
int i = 1/0;
}
}
2.3、@Transactional(propagation = Propagation.SUPPORTS)
A方法有事务,B加入到事务中
@Service
public class StudentService {
@Autowired
private StudentMapper studentMapper;
@Autowired
private CourseService courseService;
//由于courseService.insertCourse使用@Transactional(propagation = Propagation.SUPPORTS)
//所以会加入到insert0()开启的事务中,被一起回滚
@Transactional(propagation = Propagation.REQUIRED)
public void insert0(){
courseService.insertCourse();
Student student = new Student();
student.setId(1);
student.setName("student_"+ System.currentTimeMillis());
studentMapper.insert(student);
//这里抛出异常,courseService.insertCourse()也会被回滚
int i=1/0;
}
}
@Service
public class CourseService {
@Autowired
private CourseMapper courseMapper;
@Transactional(propagation = Propagation.SUPPORTS)
public void insertCourse(){
Course course = new Course();
course.setId(1);
course.setName("course_"+ System.currentTimeMillis());
courseMapper.insert(course);
}
}
A没有事务,B以无事务方式运行
@Service
public class StudentService {
@Autowired
private StudentMapper studentMapper;
@Autowired
private CourseService courseService;
//由于courseService.insertCourse使用@Transactional(propagation = Propagation.SUPPORTS)
//由于insert0没有开启事务,所以两个方法都以无事务方式运行,都会自动提交
public void insert0(){
courseService.insertCourse();
Student student = new Student();
student.setId(1);
student.setName("student_"+ System.currentTimeMillis());
studentMapper.insert(student);
//这里抛出异常,courseService.insertCourse()不会被回滚
//studentMapper.insert(student);也不会被回滚
int i=1/0;
}
}
@Service
public class CourseService {
@Autowired
private CourseMapper courseMapper;
@Transactional(propagation = Propagation.SUPPORTS)
public void insertCourse(){
Course course = new Course();
course.setId(1);
course.setName("course_"+ System.currentTimeMillis());
courseMapper.insert(course);
}
}
2.4、@Transactional(propagation = Propagation.NOT_SUPPORTED)
A有事务,B以无事务方式运行(自动提交)
@Service
public class StudentService {
@Autowired
private StudentMapper studentMapper;
@Autowired
private CourseService courseService;
//由于courseService.insertCourse使用@Transactional(propagation = Propagation.NOT_SUPPORTED)
//所以courseService.insertCourse会以无事务方式运行,自动提交
@Transactional(propagation = Propagation.REQUIRED)
public void insert0(){
courseService.insertCourse();
Student student = new Student();
student.setId(1);
student.setName("student_"+ System.currentTimeMillis());
studentMapper.insert(student);
//这里抛出异常,courseService.insertCourse()不会被回滚
//studentMapper.insert(student);会被回滚
int i=1/0;
}
}
@Service
public class CourseService {
@Autowired
private CourseMapper courseMapper;
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void insertCourse(){
Course course = new Course();
course.setId(1);
course.setName("course_"+ System.currentTimeMillis());
courseMapper.insert(course);
}
}
2.5、@Transactional(propagation = Propagation.MANDATORY)
A有事务,B加入事务中
@Service
public class StudentService {
@Autowired
private StudentMapper studentMapper;
@Autowired
private CourseService courseService;
//由于courseService.insertCourse使用@Transactional(propagation = Propagation.MANDATORY)
//所以courseService.insertCourse会加入到insert0()的事务中,一起回滚
@Transactional(propagation = Propagation.REQUIRED)
public void insert0(){
courseService.insertCourse();
Student student = new Student();
student.setId(1);
student.setName("student_"+ System.currentTimeMillis());
studentMapper.insert(student);
//这里抛出异常,courseService.insertCourse()会被回滚
//studentMapper.insert(student);会被回滚
int i=1/0;
}
}
@Service
public class CourseService {
@Autowired
private CourseMapper courseMapper;
@Transactional(propagation = Propagation.MANDATORY)
public void insertCourse(){
Course course = new Course();
course.setId(1);
course.setName("course_"+ System.currentTimeMillis());
courseMapper.insert(course);
}
}
A没有事务,B会抛出异常
@Service
public class StudentService {
@Autowired
private StudentMapper studentMapper;
@Autowired
private CourseService courseService;
//由于courseService.insertCourse使用@Transactional(propagation = Propagation.MANDATORY)
//courseService.insertCourse();会抛出异常
public void insert0(){
courseService.insertCourse();
Student student = new Student();
student.setId(1);
student.setName("student_"+ System.currentTimeMillis());
studentMapper.insert(student);
}
}
@Service
public class CourseService {
@Autowired
private CourseMapper courseMapper;
@Transactional(propagation = Propagation.MANDATORY)
public void insertCourse(){
Course course = new Course();
course.setId(1);
course.setName("course_"+ System.currentTimeMillis());
courseMapper.insert(course);
}
}
2.6、@Transactional(propagation = Propagation.NEVER)
A方法无事务,A和B都以无事务方式运行
@Service
public class StudentService {
@Autowired
private StudentMapper studentMapper;
@Autowired
private CourseService courseService;
//由于courseService.insertCourse使用@Transactional(propagation = Propagation.NEVER)
//courseService.insertCourse();以无事务方式运行,两个添加都不会被回滚
public void insert0(){
courseService.insertCourse();
Student student = new Student();
student.setId(1);
student.setName("student_"+ System.currentTimeMillis());
studentMapper.insert(student);
//这里抛出异常,但是两个添加操作都是自动提交的,都不会被回滚
int i = 1/0;
}
}
@Service
public class CourseService {
@Autowired
private CourseMapper courseMapper;
@Transactional(propagation = Propagation.NEVER)
public void insertCourse(){
Course course = new Course();
course.setId(1);
course.setName("course_"+ System.currentTimeMillis());
courseMapper.insert(course);
}
}
A方法有事务,B抛出异常
@Service
public class StudentService {
@Autowired
private StudentMapper studentMapper;
@Autowired
private CourseService courseService;
//由于courseService.insertCourse使用@Transactional(propagation = Propagation.NEVER)
//由于insert0()开启了一个事务,所以courseService.insertCourse();会抛出异常
@Transactional(propagation = Propagation.REQUIRED)
public void insert0(){
Student student = new Student();
student.setId(1);
student.setName("student_"+ System.currentTimeMillis());
studentMapper.insert(student);
//这里会抛出异常,因为courseService.insertCourse不支持事务
courseService.insertCourse();
}
}
@Service
public class CourseService {
@Autowired
private CourseMapper courseMapper;
@Transactional(propagation = Propagation.NEVER)
public void insertCourse(){
Course course = new Course();
course.setId(1);
course.setName("course_"+ System.currentTimeMillis());
courseMapper.insert(course);
}
}
2.7、@Transactional(propagation = Propagation.NESTED)
A方法抛出异常,B方法成功的提交也被回滚
@Service
public class StudentService {
@Autowired
private StudentMapper studentMapper;
@Autowired
private CourseService courseService;
//由于courseService.insertCourse使用@Transactional(propagation = Propagation.NESTED)
//由于insert0()开启了一个事务,所以courseService.insertCourse();在嵌套事务内层运行
//当courseService.insertCourse()成功提交后,insert0()最后抛出异常,会导致courseService.insertCourse()也会被回滚
@Transactional(propagation = Propagation.REQUIRED)
public void insert0(){
try {
courseService.insertCourse();
} catch (Exception e) {
e.printStackTrace();
}
Student student = new Student();
student.setId(1);
student.setName("student_"+ System.currentTimeMillis());
studentMapper.insert(student);
//这里抛出异常会导致courseService.insertCourse()成功的提交也被回滚
//studentMapper.insert(student)提交的也会被回滚
int i = 1/0;
}
}
@Service
public class CourseService {
@Autowired
private CourseMapper courseMapper;
@Transactional(propagation = Propagation.NESTED)
public void insertCourse(){
Course course = new Course();
course.setId(1);
course.setName("course_"+ System.currentTimeMillis());
courseMapper.insert(course);
}
}
A方法执行成功,B方法抛出异常,只有B的提交被回滚,A的提交成功执行
@Service
public class StudentService {
@Autowired
private StudentMapper studentMapper;
@Autowired
private CourseService courseService;
//由于courseService.insertCourse使用@Transactional(propagation = Propagation.NESTED)
//由于insert0()开启了一个事务,所以courseService.insertCourse();在嵌套事务内层运行
//当courseService.insertCourse()抛出异常会被catch,insert0会成功执行
//最后,insert0成功提交,courseService.insertCourse()被回滚了
@Transactional(propagation = Propagation.REQUIRED)
public void insert0(){
try {
courseService.insertCourse();
} catch (Exception e) {
e.printStackTrace();
}
Student student = new Student();
student.setId(1);
student.setName("student_"+ System.currentTimeMillis());
studentMapper.insert(student);
}
}
@Service
public class CourseService {
@Autowired
private CourseMapper courseMapper;
@Transactional(propagation = Propagation.NESTED)
public void insertCourse(){
Course course = new Course();
course.setId(1);
course.setName("course_"+ System.currentTimeMillis());
courseMapper.insert(course);
//这个方法抛出异常,只会回滚当前这个方法
int i = 1/0;
}
}
A方法提交成功,B方法也提交成功
@Service
public class StudentService {
@Autowired
private StudentMapper studentMapper;
@Autowired
private CourseService courseService;
//由于courseService.insertCourse使用@Transactional(propagation = Propagation.NESTED)
//由于insert0()开启了一个事务,所以courseService.insertCourse();在嵌套事务内层运行
//当courseService.insertCourse()抛出异常会被catch,insert0会成功执行
//最后,insert0成功提交,courseService.insertCourse()被回滚了
@Transactional(propagation = Propagation.REQUIRED)
public void insert0(){
try {
courseService.insertCourse();
} catch (Exception e) {
e.printStackTrace();
}
Student student = new Student();
student.setId(1);
student.setName("student_"+ System.currentTimeMillis());
studentMapper.insert(student);
}
}
@Service
public class CourseService {
@Autowired
private CourseMapper courseMapper;
@Transactional(propagation = Propagation.NESTED)
public void insertCourse(){
Course course = new Course();
course.setId(1);
course.setName("course_"+ System.currentTimeMillis());
courseMapper.insert(course);
}
}
B方法都用@Transactional(propagation = Propagation.REQUIRES_NEW)来修饰,是否也能达到这个效果呢?
@Service
public class StudentService {
@Autowired
private StudentMapper studentMapper;
@Autowired
private CourseService courseService;
@Transactional(propagation = Propagation.REQUIRED)
public void insert0(){
try {
//由于这个方法使用了@Transactional(propagation = Propagation.REQUIRES_NEW)
//尽管内部抛出异常,但是,insert0()方法的事务不会被影响
courseService.insertCourse();
} catch (Exception e) {
e.printStackTrace();
}
Student student = new Student();
student.setId(1);
student.setName("student_"+ System.currentTimeMillis());
studentMapper.insert(student);
}
}
@Service
public class CourseService {
@Autowired
private CourseMapper courseMapper;
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void insertCourse(){
Course course = new Course();
course.setId(1);
course.setName("course_"+ System.currentTimeMillis());
courseMapper.insert(course);
int i = 1/0;
}
}
2.8、无事务运行
@Service
public class StudentService {
@Autowired
private StudentMapper studentMapper;
//没有使用@Transactional注解,以无事务方式运行
//studentMapper.insert(student);自动提交,不会被回滚
public void insert0(){
Student student = new Student();
student.setId(1);
student.setName("student_"+ System.currentTimeMillis());
studentMapper.insert(student);
int i = 1/0;//这里抛出异常,但是studentMapper.insert会自动提交,不会被回滚
}
}
3、事务失效情况
3.1、使用了@Transactional的方法,被同一个类里面的无@Transactional方法调用,@Transactional的方法无效。
下面的这种情况,student表 和 source表 都能成功添加数据,回滚失败。注意:这是在同一个类中的方法相互调用,上述的是不同类的方法调用,上下文不违背!
@Service
public class StudentImpl implements IStudent {
@Autowired
public StudentMapper studentMapper;
@Autowired
public SourceMapper sourceMapper;
@Autowired
public ISource source;
@Override
public void insert0() {
Student student = new Student();
student.setId(1);
student.setName("student:123-sad");
studentMapper.insert(student);
insertProxy();
}
@Transactional
public void insertProxy(){
Source source = new Source();
source.setId(210);
source.setName("source:210=qqwe");
sourceMapper.insert(source);
int i = 1/0;
}
但是下面这种情况B方法是可以回滚成功的,但A方法不能回滚,与第二章说明的情况一致
@Service
public class StudentImpl implements IStudent {
@Autowired
public StudentMapper studentMapper;
@Autowired
public SourceMapper sourceMapper;
@Autowired
public ISource source;
@Override
public void insert0() {
Student student = new Student();
student.setId(1);
student.setName("student:123-sad");
studentMapper.insert(student);
insertProxy();
}
//无论这里有没有@Transactional注解,source.insert0()一样能回滚成功
@Transactional
public void insertProxy(){
source.insert0();
}
@Service
public class SourceImpl implements ISource {
@Autowired
public SourceMapper sourceMapper;
@Override
@Transactional
public void insert0() {
Source source = new Source();
source.setId(210);
source.setName("source:210=qqwe");
sourceMapper.insert(source);
int i = 1/0;
}
}
原因:Spring的事务管理是基于动态代理对象的代理逻辑实现的,那么如果在类内部的非事务方法调用类内部的事务方法,这个调用事务方法的过程并不是通过代理对象来调用的,而是直接通过this对象来调用方法,绕过的代理对象,肯定就是没有代理逻辑了。可以理解为使用了Spring注入的bean才会生成动态代理对象,更详细解释请看这里 https://blog.csdn.net/qq_45228323/article/details/125168679
解决方法:使用在类中注入自己的bean,加上@Lazy是为了防止bean的循环依赖注入。但是在实际开发中很少这样写,但这样确实能解决@Transactional失效的问题
@Service
public class StudentImpl implements IStudent {
@Autowired
public StudentMapper studentMapper;
@Autowired
public SourceMapper sourceMapper;
@Autowired
public ISource source;
@Autowired
@Lazy
public StudentImpl studentImpl;
@Override
public void insert0() {
Student student = new Student();
student.setId(1);
student.setName("student:123-sad");
studentMapper.insert(student);
studentImpl.insertProxy();
}
//insertProxy()方法能成功回滚,Source表没有数据,但insert0()还是会添加成功,Student表有数据
@Transactional
public void insertProxy(){
Source source1 = new Source();
source1.setId(999);
source1.setName("source:999=qqwe");
sourceMapper.insert(source1);
int i = 1/0;
}
}
区分:下面这种情况是 绝对能回滚 的,insertProxy()方法只是对代码的抽取而已,请不要搞混。
@Service
public class StudentImpl implements IStudent {
@Autowired
public StudentMapper studentMapper;
@Autowired
public SourceMapper sourceMapper;
@Autowired
public ISource source;
@Override
@Transactional
public void insert0() {
Student student = new Student();
student.setId(1);
student.setName("student:123-sad");
studentMapper.insert(student);
insertProxy();
}
private void insertProxy(){
Source source = new Source();
source.setId(210);
source.setName("source:210=qqwe");
sourceMapper.insert(source);
int i = 1/0;
}
4. 奇特的回滚方式
我们都知道,在Service层打上@Transactional后,若在该层操作出错了,不捕获异常,交由Controller层去捕获,则会回滚;但是如果异常在Service层就被捕获了,则不会回滚。
但有以下方式,即使在Service层捕获了异常,仍然可以回滚成功。
@RestController
public class InsertController {
@Autowired
public IStudent studentService;
@GetMapping("/insert")
public String insert(){
String result = "error";
try {
result = studentService.insert0();
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
}
@Service
public class StudentImpl implements IStudent {
@Autowired
public ISource sourceService;
@Override
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
public String insert0() throws Exception {
String result = sourceService.insert0();
if ("fail".equals(result)) {
throw new Exception();
}
return "OK";
}
}
@Service
public class SourceImpl implements ISource {
@Autowired
public SourceMapper sourceMapper;
@Override
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
public String insert0() throws Exception{
String result = "success";
try {
Source source = new Source();
source.setName("source:210=qqwe");
sourceMapper.insert(source);
int i = 1/0;
}catch (Exception e){
result = "fail";
}
return result;
}
}
这是因为A方法调用了B方法,A方法和B方法组成了一个事务,虽然B方法异常确实是被捕获到了,但是抛出异常的是A方法,由于A方法和B方法已经组成了一个事务了,所以这两个方法会一起回滚!