Spring事务传播机制

309 阅读10分钟

1、什么是Spring的传播机制

事务的传播,是A方法调用B方法并将事务传递给它。事务的传播机制主要针对被调用者而言,控制它是否被传播或者被怎样传播。spring事务的传播机制有七种:

  1. required:必须的 说明事务时必须的 没有就新建事务
  2. supports::支持 说明仅仅支持事务 没有事务就以非事务方法执行
  3. mandatory:强制的, 说明一定要有事务,没有事务就抛异常
  4. reuquires_new 必须新建事务,当前有事务,就将当前事务挂起
  5. not_supported: 不支持事务,如果存在事务就挂起
  6. never:绝不会有事务 如果存在事务就抛出异常
  7. nested:嵌套,当前有事务,新建一个事务嵌套到父事务中,父事务回归,新建的事务也会回滚。当前没有事务就新建一个事务

image.png

传播级别一般不需要定义,默认就是PROPAGATION_REQUIRED,除非在嵌套事务的情况。上述描述表格的描述还是比较抽象,下面我们使用一个例子来说明这个传播机制。假定方法A调用方法B:

image.png

2、七种事务传播机制

现在在这里,我们假定方法A 是 StudentService类中的方法,方法B是CourseService类中的方法

2.1、@Transactional(propagation = Propagation.REQUIRED)

A中没有事务,B中有事务。A调用B的时候,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才会生成动态代理对象

解决方法:使用在类中注入自己的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方法已经组成了一个事务了,所以这两个方法会一起回滚!