昨天在项目中碰到了@Transactional(propagation = Propagation.REQUIRES_NEW)这种事务传播机制,下面就简单回顾一下这个事务注解的使用。我主要列了如下两种
传播机制 | 说明 |
Propagation.REQUIRES_NEW | 创建一个新事务,如果存在当前事务,则挂起该事务 |
Propagation.REQUIRED | 如果当前没有事务,就创建一个事务,如果已经存在事务,就加入到这个事务。这也是@Transactional的默认级别的传播机制 |
首先测试一下Required级别的传播机制,在doRollback()方法中,调用了两个服务,第一个是添加学生成绩,第二个是添加学生。让着两个服务都加入到doRollback的这个事务中
@Service
@Slf4j
public class TransactionalServiceImpl implements TransactionalService {
@Autowired
StudentService studentService;
@Autowired
SchoolReportService schoolReportService;
@Override
@Transactional
public Result doRollback() {
try {
schoolReportService.addSchoolReport();
studentService.addStudent();
} catch (Exception e) {
log.info("studentService发生异常啦");
}
return new Result("ok", true);
}
}
我这边让SchoolReportService正常执行,StudentService发生异常
@Service
public class SchoolReportServiceImpl implements SchoolReportService {
@Autowired
SchoolReportMapper schoolReportMapper;
@Override
public Result addSchoolReport() {
SchoolReportPO schoolReportPO = new SchoolReportPO();
Random random = new Random();
schoolReportPO.setCnScore(random.nextInt(3));
schoolReportMapper.insert(schoolReportPO);
return new Result("ok",true);
}
}
@Service
public class StudentServiceImpl implements StudentService {
@Autowired
StudentMapper studentMapper;
@Override
public Result addStudent() {
StudentPO studentPO = new StudentPO();
studentPO.setName(String.valueOf(Math.random()));
studentMapper.insert(studentPO);
int a = 10 / 0;
return new Result("ok",true);
}
}
我们期望的结果应该是:school_report的这张表数据发生回滚,student表中数据也发生回滚。
而结果school_report这张表成功插入数据,student表也成功插入数据。
原来方法上未加任何属性的@Transactional注解会在抛出RuntimeException或者Error时触发事务的回滚,但是catch住了,在doRollback()方法中是不会发生事务回滚的。
但有时候我们的需求是需要将异常catch住,并且做一些错误日志的插入的业务操作。那么其实我们可以进行手动回滚。通过TransactionAspectSupport这个类设置一个回滚点,一旦发生异常那么我们可以直接回滚到最初设置好的位置
@Override
@Transactional
public Result doRollback() {
Object savePoint = null;
try {
savePoint =
TransactionAspectSupport.currentTransactionStatus().createSavepoint();
schoolReportService.addSchoolReport();
studentService.addStudent();
} catch (Exception e) {
log.info("studentService发生异常啦",e);
TransactionAspectSupport.currentTransactionStatus().rollbackToSavepoint(savePoint);
}
return new Result("ok", true);
}
下面开始讲一下,Propagation.REQUIRES_NEW这种传播机制的使用场景。还是上面这种场景,我们想要SchoolReportService不发生回滚,StudentService发生回滚,那么怎么处理呢?
我们只要在addStudent()方法上添加REQUIRES_NEW的传播机制
@Service
public class StudentServiceImpl implements StudentService {
@Autowired
StudentMapper studentMapper;
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW)
public Result addStudent() {
StudentPO studentPO = new StudentPO();
studentPO.setName(String.valueOf(Math.random()));
studentMapper.insert(studentPO);
int a = 10 / 0;
return new Result("ok",true);
}
}
那么我们可以测试一下,稍微改动一下,schoolReportService不做任何改变。发现school_report表正常插入一条数据,student表发生回滚。
@Override
@Transactional
public Result notGlobalRollback() {
schoolReportService.addSchoolReport();
try {
studentService.addStudent();
}catch (Exception e){
log.info("studentService发生异常啦");
}
return new Result("ok", true);
}
我们再来改变一下,schoolReportService发生异常,studentService不发生异常,会不会相互影响
将两者服务的执行顺序改变一下。发现student表成功插入,school_report成功回滚。发现REQUIRES.NEW确实有独立事务隔离的作用
@Override
@Transactional
public Result notGlobalRollback() {
studentService.addStudent();
schoolReportService.addSchoolReport();
return new Result("ok", true);
}
}
@Service
public class SchoolReportServiceImpl implements SchoolReportService {
@Autowired
SchoolReportMapper schoolReportMapper;
@Override
public Result addSchoolReport() {
SchoolReportPO schoolReportPO = new SchoolReportPO();
Random random = new Random();
schoolReportPO.setCnScore(random.nextInt(3));
schoolReportMapper.insert(schoolReportPO);
int a = 10 / 0;
return new Result("ok",true);
}
}
@Service
public class StudentServiceImpl implements StudentService {
@Autowired
StudentMapper studentMapper;
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW)
public Result addStudent() {
StudentPO studentPO = new StudentPO();
studentPO.setName(String.valueOf(Math.random()));
studentMapper.insert(studentPO);
return new Result("ok",true);
}
}