Spring事务传播机制详解和 @Transactional 失效情况原因及解决方法
什么是事务传播机制
-
事务的传播,是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无事务
七种事务传播机制
- 现在在这里,我们假定
方法A
是StudentService
类中的方法,方法B
是CourseService
类中的方法
@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); } }
@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; } }
@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); } }
@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); } }
@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); } }
@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); } }
@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; } }
无事务运行
-
@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会自动提交,不会被回滚 } }
事务失效情况
使用了@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对象来调用方法,绕过的代理对象,肯定就是没有代理逻辑了。
-
解决方法:使用在类中注入自己的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; } }
奇特的回滚方式
-
我们都知道,在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方法已经组成了一个事务了,所以这两个方法会一起回滚!