一文彻底弄懂spring事务的七种传播机制

554 阅读12分钟

一、Spring中七种事务传播行为

事务传播行为类型说明
PROPAGATION_REQUIRED如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。
PROPAGATION_SUPPORTS支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY使用当前的事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。

二、准备工作

创建两张表,学生表和教师表

CREATE TABLE `student` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `age` int(11) DEFAULT NULL,
  `name` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
​
CREATE TABLE `teacher` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

创建对应实体类,mapper,以及service。

@Data
@TableName("student")
@Accessors(chain = true)
public class Student {
​
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;
    
    @TableField(value = "age")
    private Long age;
    
    @TableField(value = "name")
    private String name;
​
}
​
@Data
@TableName("teacher")
@Accessors(chain = true)
public class Teacher {
​
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;
​
    @TableField(value = "name")
    private String name;
​
}
​
@Mapper
public interface StudentMapper extends BaseMapper<Student> {
    
}
​
​
@Mapper
public interface TeacherMapper extends BaseMapper<Teacher> {
}
​
​
@Service
@Slf4j
public class TeacherServiceImpl implements TeacherService {
​
    private final TeacherMapper teacherMapper;
​
    private final StudentService studentService;
​
    public TeacherServiceImpl(TeacherMapper teacherMapper, StudentService studentService) {
        this.teacherMapper = teacherMapper;
        this.studentService = studentService;
    }
​
    @Override
    public void addTeacher() {
​
    }
}
​
@Service
@Slf4j
public class StudentServiceImpl implements StudentService {
    private final StudentMapper studentMapper;
​
    public StudentServiceImpl(StudentMapper studentMapper) {
        this.studentMapper = studentMapper;
    }
​
​
    @Override
    public void addStudent() {
        studentMapper.insert(new Student().setName("小明"));
    }
}
​

三、各种传播行为模拟

模拟一个事务(插入一条教师记录,一条学生记录),此外,每次经过测试之后,都会清空数据库对应的表记录。

1、没有事务支持

@Service
@Slf4j
public class TeacherServiceImpl implements TeacherService {
​
    private final TeacherMapper teacherMapper;
​
    private final StudentService studentService;
​
    public TeacherServiceImpl(TeacherMapper teacherMapper, StudentService studentService) {
        this.teacherMapper = teacherMapper;
        this.studentService = studentService;
    }
​
    @Override
    public void addTeacher() {
        teacherMapper.insert(new Teacher().setName("周老师"));
​
        studentService.addStudent();
    }
}
​
@Service
@Slf4j
public class StudentServiceImpl implements StudentService {
    private final StudentMapper studentMapper;
​
    public StudentServiceImpl(StudentMapper studentMapper) {
        this.studentMapper = studentMapper;
    }
​
​
    @Override
    public void addStudent() {
        studentMapper.insert(new Student().setName("小明"));
        throw new RuntimeException("业务异常");
    }
}
​
​

可以看到,没有事务的支持,因为没有事务的支持,即使在最后抛出的异常,数据依然是正常入库了。

image-20240703165540361.png

image-20240703165633392.png

2、PROPAGATION_REQUIRED

propagation(n.传播;扩展;宣传;培养)

required(v 需要;(尤指根据法规)规定;使做(某事);使拥有(某物);依赖;依靠 ;adj 必修的)

顾英文名思意,就需要传播。这种传播机制也是我们工作中用的最多的。

2.1 场景一: 外层方法增加事务,内层方法不加
@Service
@Slf4j
public class TeacherServiceImpl implements TeacherService {
​
    private final TeacherMapper teacherMapper;
​
    private final StudentService studentService;
​
    public TeacherServiceImpl(TeacherMapper teacherMapper, StudentService studentService) {
        this.teacherMapper = teacherMapper;
        this.studentService = studentService;
    }
​
    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void addTeacher() {
        log.info("新增教师方法是否存在事务{}", TransactionSynchronizationManager.isActualTransactionActive());
        log.info("新增教师方法事务名称为{}", TransactionSynchronizationManager.getCurrentTransactionName());
​
        teacherMapper.insert(new Teacher().setName("周老师"));
​
        studentService.addStudent();
    }
}
​
@Service
@Slf4j
public class StudentServiceImpl implements StudentService {
    private final StudentMapper studentMapper;
​
    public StudentServiceImpl(StudentMapper studentMapper) {
        this.studentMapper = studentMapper;
    }
​
​
    @Override
    public void addStudent() {
        log.info("新增学生方法是否存在事务{}", TransactionSynchronizationManager.isActualTransactionActive());
        log.info("新增学生方法事务名称为{}", TransactionSynchronizationManager.getCurrentTransactionName());
        studentMapper.insert(new Student().setName("小明"));
        throw new RuntimeException("业务异常");
    }
}

数据库结果

image-20240703170921049.png

image-20240703170933119.png

控制台打印结果

image-20240703171028727.png

可以看到事务生效,异常导致前面插入的两条记录都回滚掉了,并且addStudent()加入了外层addTeacher()的事务,打印了相同的事务名。

2.2 场景二: 外层方法不加事务,内层方法加
@Service
@Slf4j
public class TeacherServiceImpl implements TeacherService {
​
    private final TeacherMapper teacherMapper;
​
    private final StudentService studentService;
​
    public TeacherServiceImpl(TeacherMapper teacherMapper, StudentService studentService) {
        this.teacherMapper = teacherMapper;
        this.studentService = studentService;
    }
​
    @Override
    public void addTeacher() {
        log.info("新增教师方法是否存在事务{}", TransactionSynchronizationManager.isActualTransactionActive());
        log.info("新增教师方法事务名称为{}", TransactionSynchronizationManager.getCurrentTransactionName());
​
        teacherMapper.insert(new Teacher().setName("周老师"));
​
        studentService.addStudent();
    }
}
​
@Service
@Slf4j
public class StudentServiceImpl implements StudentService {
    private final StudentMapper studentMapper;
​
    public StudentServiceImpl(StudentMapper studentMapper) {
        this.studentMapper = studentMapper;
    }
​
​
    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void addStudent() {
        log.info("新增学生方法是否存在事务{}", TransactionSynchronizationManager.isActualTransactionActive());
        log.info("新增学生方法事务名称为{}", TransactionSynchronizationManager.getCurrentTransactionName());
        studentMapper.insert(new Student().setName("小明"));
        throw new RuntimeException("业务异常");
    }
}

数据库结果

image-20240703165540361.png

image-20240703170933119.png

控制台打印结果

image-20240703172451690.png

可以看到,在外层方法addTeacher()没有事务的时候,内层方法addStudent()自己新建一个事务,所以内层方法因为异常回滚导致学生记录回滚掉了,而外层方法因为没有事务支持,即使报错了,教师记录还是新增成功了。

2.3 场景三: 外内层公用一个事务,且内层异常捕获掉。
@Service
@Slf4j
public class TeacherServiceImpl implements TeacherService {
​
    private final TeacherMapper teacherMapper;
​
    private final StudentService studentService;
​
    public TeacherServiceImpl(TeacherMapper teacherMapper, StudentService studentService) {
        this.teacherMapper = teacherMapper;
        this.studentService = studentService;
    }
​
    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void addTeacher() {
        log.info("新增教师方法是否存在事务{}", TransactionSynchronizationManager.isActualTransactionActive());
        log.info("新增教师方法事务名称为{}", TransactionSynchronizationManager.getCurrentTransactionName());
​
        teacherMapper.insert(new Teacher().setName("周老师"));
​
        studentService.addStudent();
    }
}
​
@Service
@Slf4j
public class StudentServiceImpl implements StudentService {
    private final StudentMapper studentMapper;
​
    public StudentServiceImpl(StudentMapper studentMapper) {
        this.studentMapper = studentMapper;
    }
​
​
    @Override
    public void addStudent() {
        log.info("新增学生方法是否存在事务{}", TransactionSynchronizationManager.isActualTransactionActive());
        log.info("新增学生方法事务名称为{}", TransactionSynchronizationManager.getCurrentTransactionName());
        studentMapper.insert(new Student().setName("小明"));
​
        try {
            throw new RuntimeException("业务异常");
        } catch (RuntimeException e) {
        }
    }
}
​

数据库结果

image-20240703165540361.png

image-20240703165633392.png 控制台打印结果

image-20240703171028727.png

可以看到这种情况下,内外层方法都是有事务控制的,而异常被捕获,相当一段正常的代码,所以学生和教师两条记录都插入成功了。而即使把捕获异常的那段代码丢到外层方法,实现的效果也是一样的。

3、PROPAGATION_SUPPORTS

3.1 场景一: 外层方法有事务
@Service
@Slf4j
public class TeacherServiceImpl implements TeacherService {
​
    private final TeacherMapper teacherMapper;
​
    private final StudentService studentService;
​
    public TeacherServiceImpl(TeacherMapper teacherMapper, StudentService studentService) {
        this.teacherMapper = teacherMapper;
        this.studentService = studentService;
    }
​
    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void addTeacher() {
        log.info("新增教师方法是否存在事务{}", TransactionSynchronizationManager.isActualTransactionActive());
        log.info("新增教师方法事务名称为{}", TransactionSynchronizationManager.getCurrentTransactionName());
​
        teacherMapper.insert(new Teacher().setName("周老师"));
​
        studentService.addStudent();
    }
}
​
@Service
@Slf4j
public class StudentServiceImpl implements StudentService {
    private final StudentMapper studentMapper;
​
    public StudentServiceImpl(StudentMapper studentMapper) {
        this.studentMapper = studentMapper;
    }
​
​
    @Override
    @Transactional(propagation = Propagation.SUPPORTS)
    public void addStudent() {
        log.info("新增学生方法是否存在事务{}", TransactionSynchronizationManager.isActualTransactionActive());
        log.info("新增学生方法事务名称为{}", TransactionSynchronizationManager.getCurrentTransactionName());
        studentMapper.insert(new Student().setName("小明"));
​
        try {
            throw new RuntimeException("业务异常");
        } catch (RuntimeException e) {
        }
    }
}
​

控制台打印结果

image-20240703171028727.png

可以看到,这时候内层事务加入到了外层事务,这种情况和required是一致的。

3.2 场景二: 外层方法没有事务
@Service
@Slf4j
public class TeacherServiceImpl implements TeacherService {
​
    private final TeacherMapper teacherMapper;
​
    private final StudentService studentService;
​
    public TeacherServiceImpl(TeacherMapper teacherMapper, StudentService studentService) {
        this.teacherMapper = teacherMapper;
        this.studentService = studentService;
    }
​
    @Override
    public void addTeacher() {
        log.info("新增教师方法是否存在事务{}", TransactionSynchronizationManager.isActualTransactionActive());
        log.info("新增教师方法事务名称为{}", TransactionSynchronizationManager.getCurrentTransactionName());
​
        teacherMapper.insert(new Teacher().setName("周老师"));
​
        studentService.addStudent();
    }
}
​
​
@Service
@Slf4j
public class StudentServiceImpl implements StudentService {
    private final StudentMapper studentMapper;
​
    public StudentServiceImpl(StudentMapper studentMapper) {
        this.studentMapper = studentMapper;
    }
​
​
    @Override
    @Transactional(propagation = Propagation.SUPPORTS)
    public void addStudent() {
        log.info("新增学生方法是否存在事务{}", TransactionSynchronizationManager.isActualTransactionActive());
        log.info("新增学生方法事务名称为{}", TransactionSynchronizationManager.getCurrentTransactionName());
        studentMapper.insert(new Student().setName("小明"));
        throw new RuntimeException("业务异常");
    }
}
​

数据库结果 image-20240703165540361.png image-20240703165633392.png 控制台打印结果 image-20240703175006040.png 可以看到虽然内层方法打印了事务名,但这时候是没有事务支持的,导致学生和教师的记录即使在有异常抛出的情况下也插入成功了。

4、PROPAGATION_MANDATORY

mandatory adj.强制性的;强制的;法定的;义务的 /ˈmændətəri/ 来,跟我一起读,忙得特瑞,欸,好。很强硬,必要要有事务,没有就罢工。

4.1 场景一: 外层方法有事务
@Service
@Slf4j
public class TeacherServiceImpl implements TeacherService {
​
    private final TeacherMapper teacherMapper;
​
    private final StudentService studentService;
​
    public TeacherServiceImpl(TeacherMapper teacherMapper, StudentService studentService) {
        this.teacherMapper = teacherMapper;
        this.studentService = studentService;
    }
​
    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void addTeacher() {
        log.info("新增教师方法是否存在事务{}", TransactionSynchronizationManager.isActualTransactionActive());
        log.info("新增教师方法事务名称为{}", TransactionSynchronizationManager.getCurrentTransactionName());
​
        teacherMapper.insert(new Teacher().setName("周老师"));
​
        studentService.addStudent();
    }
}
​
@Service
@Slf4j
public class StudentServiceImpl implements StudentService {
    private final StudentMapper studentMapper;
​
    public StudentServiceImpl(StudentMapper studentMapper) {
        this.studentMapper = studentMapper;
    }
​
​
    @Override
    @Transactional(propagation = Propagation.MANDATORY)
    public void addStudent() {
        log.info("新增学生方法是否存在事务{}", TransactionSynchronizationManager.isActualTransactionActive());
        log.info("新增学生方法事务名称为{}", TransactionSynchronizationManager.getCurrentTransactionName());
        studentMapper.insert(new Student().setName("小明"));
        throw new RuntimeException("业务异常");
    }
}
​
​

数据库结果 image-20240703170921049.png image-20240703170933119.png 控制台打印结果 image-20240703171028727.png

可以看到,这种情况和

可以看到,这时候内层事务加入到了外层事务,这种情况和required和SUPPORTS 是一致的。

4.2 场景二: 外层方法没有事务
@Service
@Slf4j
public class TeacherServiceImpl implements TeacherService {
​
    private final TeacherMapper teacherMapper;
​
    private final StudentService studentService;
​
    public TeacherServiceImpl(TeacherMapper teacherMapper, StudentService studentService) {
        this.teacherMapper = teacherMapper;
        this.studentService = studentService;
    }
​
    @Override
    public void addTeacher() {
        log.info("新增教师方法是否存在事务{}", TransactionSynchronizationManager.isActualTransactionActive());
        log.info("新增教师方法事务名称为{}", TransactionSynchronizationManager.getCurrentTransactionName());
​
        teacherMapper.insert(new Teacher().setName("周老师"));
​
        studentService.addStudent();
    }
}
​
​
@Service
@Slf4j
public class StudentServiceImpl implements StudentService {
    private final StudentMapper studentMapper;
​
    public StudentServiceImpl(StudentMapper studentMapper) {
        this.studentMapper = studentMapper;
    }
​
​
    @Override
    @Transactional(propagation = Propagation.MANDATORY)
    public void addStudent() {
        log.info("新增学生方法是否存在事务{}", TransactionSynchronizationManager.isActualTransactionActive());
        log.info("新增学生方法事务名称为{}", TransactionSynchronizationManager.getCurrentTransactionName());
        studentMapper.insert(new Student().setName("小明"));
        throw new RuntimeException("业务异常");
    }
}
​
​

控制台打印结果

image-20240704101511359.png

可以看到,因为外层没有事务,走到内层方法得时候抛出非法的事务状态异常。

5、PROPAGATION_REQUIRES_NEW

REQUIRES_NEW 需要一个新的,不管外层方法咋样,内层都需要一个新的。

5.1 场景一: 外层方法有事务
@Service
@Slf4j
public class TeacherServiceImpl implements TeacherService {
​
    private final TeacherMapper teacherMapper;
​
    private final StudentService studentService;
​
    public TeacherServiceImpl(TeacherMapper teacherMapper, StudentService studentService) {
        this.teacherMapper = teacherMapper;
        this.studentService = studentService;
    }
​
    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void addTeacher() {
        log.info("新增教师方法是否存在事务{}", TransactionSynchronizationManager.isActualTransactionActive());
        log.info("新增教师方法事务名称为{}", TransactionSynchronizationManager.getCurrentTransactionName());
​
        teacherMapper.insert(new Teacher().setName("周老师"));
​
        studentService.addStudent();
    }
}
​
​
@Service
@Slf4j
public class StudentServiceImpl implements StudentService {
    private final StudentMapper studentMapper;
​
    public StudentServiceImpl(StudentMapper studentMapper) {
        this.studentMapper = studentMapper;
    }
​
​
    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void addStudent() {
        log.info("新增学生方法是否存在事务{}", TransactionSynchronizationManager.isActualTransactionActive());
        log.info("新增学生方法事务名称为{}", TransactionSynchronizationManager.getCurrentTransactionName());
        studentMapper.insert(new Student().setName("小明"));
        throw new RuntimeException("业务异常");
    }
}
​

控制台打印结果

image-20240704102321214.png

可以看到,内外层方法是由不同的事务支持,但这时候数据库都没有插入成功,因为内层异常导致内层数据回滚,然后异常又抛到外层,导致外层数据回滚。那这时候又有聪明的同学问了,欸,有没有什么办法让内层回滚,外层数据正常插入了呢,当然可以,外层捕获异常就可以了,代码如下

@Override
@Transactional(propagation = Propagation.REQUIRED)
public void addTeacher() {
    log.info("新增教师方法是否存在事务{}", TransactionSynchronizationManager.isActualTransactionActive());
    log.info("新增教师方法事务名称为{}", TransactionSynchronizationManager.getCurrentTransactionName());
​
    teacherMapper.insert(new Teacher().setName("周老师"));
​
    try {
        studentService.addStudent();
    } catch (Exception e) {
    }
}

这种一般用于某些业务的日志记录场景,外层事务就可以在try和catch两个地方记录操作成功的日志和操作失败的日志。

5.1 场景二: 外层方法没有事务
@Service
@Slf4j
public class TeacherServiceImpl implements TeacherService {
​
    private final TeacherMapper teacherMapper;
​
    private final StudentService studentService;
​
    public TeacherServiceImpl(TeacherMapper teacherMapper, StudentService studentService) {
        this.teacherMapper = teacherMapper;
        this.studentService = studentService;
    }
​
    @Override
    public void addTeacher() {
        log.info("新增教师方法是否存在事务{}", TransactionSynchronizationManager.isActualTransactionActive());
        log.info("新增教师方法事务名称为{}", TransactionSynchronizationManager.getCurrentTransactionName());
        teacherMapper.insert(new Teacher().setName("周老师"));
        studentService.addStudent();
    }
}
​
@Service
@Slf4j
public class StudentServiceImpl implements StudentService {
    private final StudentMapper studentMapper;
​
    public StudentServiceImpl(StudentMapper studentMapper) {
        this.studentMapper = studentMapper;
    }
​
​
    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void addStudent() {
        log.info("新增学生方法是否存在事务{}", TransactionSynchronizationManager.isActualTransactionActive());
        log.info("新增学生方法事务名称为{}", TransactionSynchronizationManager.getCurrentTransactionName());
        studentMapper.insert(new Student().setName("小明"));
        throw new RuntimeException("业务异常");
    }
}
​
​

数据库结果

image-20240703165540361.png

image-20240703170933119.png 控制台打印结果

image-20240704104040970.png

这种情况是required没有外层事务时是一样的,内层自己新建事务,所以异常导致内层数据回滚,而外层没有事务导致数据插入成功。

6、PROPAGATION_NOT_SUPPORTED

NOT_SUPPORTED 不支持事务,外层没有事务,挺好,内层我也不要;外层有事务,我不理解,但是我不接受,内层将外层事务挂起,内层以非事务执行。

6.1 场景一: 外层方法有事务
@Service
@Slf4j
public class TeacherServiceImpl implements TeacherService {
​
    private final TeacherMapper teacherMapper;
​
    private final StudentService studentService;
​
    public TeacherServiceImpl(TeacherMapper teacherMapper, StudentService studentService) {
        this.teacherMapper = teacherMapper;
        this.studentService = studentService;
    }
​
    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void addTeacher() {
        log.info("新增教师方法是否存在事务{}", TransactionSynchronizationManager.isActualTransactionActive());
        log.info("新增教师方法事务名称为{}", TransactionSynchronizationManager.getCurrentTransactionName());
        teacherMapper.insert(new Teacher().setName("周老师"));
        studentService.addStudent();
    }
}
​
​
@Service
@Slf4j
public class StudentServiceImpl implements StudentService {
    private final StudentMapper studentMapper;
​
    public StudentServiceImpl(StudentMapper studentMapper) {
        this.studentMapper = studentMapper;
    }
​
​
    @Override
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public void addStudent() {
        log.info("新增学生方法是否存在事务{}", TransactionSynchronizationManager.isActualTransactionActive());
        log.info("新增学生方法事务名称为{}", TransactionSynchronizationManager.getCurrentTransactionName());
        studentMapper.insert(new Student().setName("小明"));
        throw new RuntimeException("业务异常");
    }
}
​

数据库结果

image-20240703170921049.png

image-20240703165633392.png

控制台打印结果

image-20240704110652543.png

这种情况下,内层方法将外层事务挂起,不走事务,所以内层记录插入成功,而外层方法由于内层的异常导致记录回滚。

6.1 场景二: 外层方法没有事务
@Service
@Slf4j
public class TeacherServiceImpl implements TeacherService {
​
    private final TeacherMapper teacherMapper;
​
    private final StudentService studentService;
​
    public TeacherServiceImpl(TeacherMapper teacherMapper, StudentService studentService) {
        this.teacherMapper = teacherMapper;
        this.studentService = studentService;
    }
​
    @Override
    public void addTeacher() {
        log.info("新增教师方法是否存在事务{}", TransactionSynchronizationManager.isActualTransactionActive());
        log.info("新增教师方法事务名称为{}", TransactionSynchronizationManager.getCurrentTransactionName());
        teacherMapper.insert(new Teacher().setName("周老师"));
        studentService.addStudent();
    }
}
​
@Service
@Slf4j
public class StudentServiceImpl implements StudentService {
    private final StudentMapper studentMapper;
​
    public StudentServiceImpl(StudentMapper studentMapper) {
        this.studentMapper = studentMapper;
    }
​
​
    @Override
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public void addStudent() {
        log.info("新增学生方法是否存在事务{}", TransactionSynchronizationManager.isActualTransactionActive());
        log.info("新增学生方法事务名称为{}", TransactionSynchronizationManager.getCurrentTransactionName());
        studentMapper.insert(new Student().setName("小明"));
        throw new RuntimeException("业务异常");
    }
}
​

数据库结果

image-20240703165540361.png

image-20240703165633392.png

控制台打印结果

image-20240704110048727.png

可以看到,这时候内外层都没有事务支持,所以即使异常抛出了,但由于是在最后面抛出的,所以两条记录都插入成功了。

7、PROPAGATION_NEVER

NEVER adv. 从不;从未;绝不

绝不,休想,我绝不可能爱你(I can never love you),

你休想(Never on your life)

外层有事务,内层支持抛出异常,都别活了!好好好,可以不爱,请别伤害哇。

@Service
@Slf4j
public class TeacherServiceImpl implements TeacherService {
​
    private final TeacherMapper teacherMapper;
​
    private final StudentService studentService;
​
    public TeacherServiceImpl(TeacherMapper teacherMapper, StudentService studentService) {
        this.teacherMapper = teacherMapper;
        this.studentService = studentService;
    }
​
    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void addTeacher() {
        log.info("新增教师方法是否存在事务{}", TransactionSynchronizationManager.isActualTransactionActive());
        log.info("新增教师方法事务名称为{}", TransactionSynchronizationManager.getCurrentTransactionName());
        teacherMapper.insert(new Teacher().setName("周老师"));
        studentService.addStudent();
    }
}
​
@Service
@Slf4j
public class StudentServiceImpl implements StudentService {
    private final StudentMapper studentMapper;
​
    public StudentServiceImpl(StudentMapper studentMapper) {
        this.studentMapper = studentMapper;
    }
​
​
    @Override
    @Transactional(propagation = Propagation.NEVER)
    public void addStudent() {
        log.info("新增学生方法是否存在事务{}", TransactionSynchronizationManager.isActualTransactionActive());
        log.info("新增学生方法事务名称为{}", TransactionSynchronizationManager.getCurrentTransactionName());
        studentMapper.insert(new Student().setName("小明"));
        throw new RuntimeException("业务异常");
    }
}
​
​
​

控制台打印结果

image-20240704111534594.png

可以看到,因为外层有事务,走到内层方法得时候抛出非法的事务状态异常。

8、PROPAGATION_NESTED

NESTED abbr.嵌套的;

PROPAGATION_NESTED 开始一个 "嵌套的" 事务, 它是已经存在事务的一个真正的子事务. 潜套事务开始执行时, 它将取得一个 savepoint. 如果这个嵌套事务失败, 我们将回滚到此 savepoint. 嵌套事务是外部事务的一部分, 只有外部事务结束后它才会被提交。 由此可见, PROPAGATION_REQUIRES_NEW 和 PROPAGATION_NESTED 的最大区别在于, PROPAGATION_REQUIRES_NEW 完全是一个新的事务, 而 PROPAGATION_NESTED 则是外部事务的子事务, 如果外部事务 commit, 嵌套事务也会被 commit, 这个规则同样适用于 roll back。

四、总结

  • PROPAGATION_REQUIRED 就是需要,有就加入,没有就新建一个。
  • PROPAGATION_SUPPORTS 支持你,支持外层,外层有,俺也有,外层没有,俺也不要了
  • PROPAGATION_MANDATORY 很强硬的,外层你有,大家就好好的,你没有,都别活了,我要抛异常!
  • PROPAGATION_REQUIRES_NEW 内层方法:你外层爱咋咋地,我自己新建一个事务
  • PROPAGATION_NOT_SUPPORTED 不支持,不管外层有没有,我都以非事务执行,比较温和,你有我就把你挂起
  • PROPAGATION_NEVER 又来一个强硬的主儿,不允许外层有事务,休想!
  • PROPAGATION_NESTED 嵌套事务,成为外层的一个子事务。