是什么
当多个含有事务的方法进行嵌套调用时,多个方法处理事务的规则
传播行为
PROCPAGATION_REQUIRED
理论
- 如果外层方法开启了事务,内层方法就会加入到外层事务
- 如果外层方法未开启事务,内层方法就会开启新的事务
可保证多个嵌套的事务方法在同一个事务内执行,也就是保证多个事务方法同时提交、同时回滚,这个机制可以满足大多数业务场景。
实例
@Service
public class ServiceA {
@Autowired
private SerivceB serbiceB;
@Transactional(rollbackFor = Exception.class, propagation = Porpagation.REQUIRED)
public void methodA() {
//1.1 A方法入库操作
insert();
Systome.out.println("insert something to db");
//1.2 调用B方法入库
serviceB.methodB();
}
}
@Serivce
public class ServiceB {
@Transactional(rollbackFor = Exception.class, propagation = Porpagation.REQUIRED)
public void methodB() {
insert();
Systome.out.println("insert something to db");
//抛出异常
//throw new RuntiomeException();
}
}
B方法传播性是REQUIRED,所以methodB会合并到methodA开启的事务中执行,当B方法出异常并回滚后,A方法操作也会回滚。
问:如果B方法中的异常使用try catch捕获后,A方法还回回滚么?
会回滚,因为REQUIRED传播性的语义就是嵌套调用的多个事务方法在同一个事务内执行,而事务本身是有原子性的,有一个事务方法抛出异常回滚了,那么所有的方法都会回滚。
PROCPAGATION_REQUIRED_NEW
理论
每次都开启新的事务,如果外层调用方已经开启了事务,就先把外层事务挂起,执行新事务,执行完毕后再恢复上层事务的执行。
实例
@Service
public class ServiceA {
@Autowired
private SerivceB serbiceB;
@Transactional(rollbackFor = Exception.class, propagation = Porpagation.REQUIRED)
public void methodA() {
//1.1 A方法入库操作
insert();
Systome.out.println("insert something to db");
//1.2 调用B方法入库
serviceB.methodB();
}
}
@Serivce
public class ServiceB {
@Transactional(rollbackFor = Exception.class, propagation = Porpagation.REQUIRED_NEW)
public void methodB() {
insert();
Systome.out.println("insert something to db");
//抛出异常
//throw new RuntiomeException();
}
}
执行流程图:
由于A方法与B方法未在一个事务中执行,当B方法出现异常并回滚后,方法A的入库操作不受影响。
PROCPAGATION_SUPPORTE
理论
- 如果外层方开启了事务,那么当前方法就加入到外层事务
- 如果外层方未开启事务,那么当前方法也不会创建新事务
实例
@Service
public class ServiceA {
@Autowired
private SerivceB serbiceB;
@Transactional(rollbackFor = Exception.class, propagation = Porpagation.REQUIRED)
public void methodA() {
//1.1 A方法入库操作
insert();
Systome.out.println("insert something to db");
//1.2 调用B方法入库
serviceB.methodB();
}
}
@Serivce
public class ServiceB {
@Transactional(rollbackFor = Exception.class, propagation = Porpagation.SUPPORTE)
public void methodB() {
insert();
Systome.out.println("insert something to db");
//抛出异常
//throw new RuntiomeException();
}
}
如果方法A本身开启了事务(传播性为REQUIRED),那么方法B会加入方法A的事务中。
如果方法A未开启事务,那么方法B也不会开启事务,在无事务的情况下执行。
PROCPAGATION_SUPPORTED
理论
不支持事务,如果外层方法开启了事务,那么当前方法以非事务的方式执行当前方法逻辑,执行完毕后,再恢复外层事务的执行。
实例
@Service
public class ServiceA {
@Autowired
private SerivceB serbiceB;
@Transactional(rollbackFor = Exception.class, propagation = Porpagation.REQUIRED)
public void methodA() {
//1.1 A方法入库操作
insert();
Systome.out.println("insert something to db");
//1.2 调用B方法入库
serviceB.methodB();
}
}
@Serivce
public class ServiceB {
@Transactional(rollbackFor = Exception.class, propagation = Porpagation.NOT_SUPPORTED)
public void methodB() {
insert();
Systome.out.println("insert something to db");
//抛出异常
//throw new RuntiomeException();
}
}
执行流程图:
PROCPAGATION_NEVER
不支持事务,如果外层方法开启了事务,当前方法会抛出异常。
实例
@Service
public class ServiceA {
@Autowired
private SerivceB serbiceB;
@Transactional(rollbackFor = Exception.class, propagation = Porpagation.REQUIRED)
public void methodA() {
//1.1 A方法入库操作
insert();
Systome.out.println("insert something to db");
//1.2 调用B方法入库
serviceB.methodB();
}
}
@Serivce
public class ServiceB {
@Transactional(rollbackFor = Exception.class, propagation = Porpagation.NEVER)
public void methodB() {
insert();
Systome.out.println("insert something to db");
//抛出异常
//throw new RuntiomeException();
}
}
方法A调用方法B时,由于方法B的传播性为NEVER,B方法会抛出异常。
PROCPAGATION_MANDATORY
理论
配置了该传播性的方法只能在已经存在事务的方法中被调用,否则会抛出异常。
实例
@Service
public class ServiceA {
@Autowired
private SerivceB serbiceB;
public void methodA() {
//1.1 A方法入库操作
insert();
Systome.out.println("insert something to db");
//1.2 调用B方法入库
serviceB.methodB();
}
}
@Serivce
public class ServiceB {
@Transactional(rollbackFor = Exception.class, propagation = Porpagation.NEVER)
public void methodB() {
insert();
Systome.out.println("insert something to db");
//抛出异常
//throw new RuntiomeException();
}
}
方法A未开启事务,调用了方法B时,会报异常。
PROCPAGATION_NESTED
理论
嵌套调用
- 当外层调用方存在事务,当前方法会合并到外层事务
- 当外层调用方不存在事务,当前方法会开启事务
该传播性与REQUIRED传播性一致,不同点是,该传播行为可以保存状态保存点,当事务回滚时,可以回滚到某一个保存点上,避免所有的事务都回滚。
实例
@Service
public class ServiceA {
@Autowired
private SerivceB serbiceB;
@Transactional(rollbackFor = Exception.class, propagation = Porpagation.REQUIRED)
public void methodA() {
//1.1 A方法入库操作
insert();
Systome.out.println("insert something to db");
//1.2 调用B方法入库
try {
serviceB.methodB();
} catch(Exception e){
}
//1.3 执行更新操作
udpate();
}
}
@Serivce
public class ServiceB {
@Transactional(rollbackFor = Exception.class, propagation = Porpagation.NESTED)
public void methodB() {
insert();
Systome.out.println("insert something to db");
//抛出异常
//throw new RuntiomeException();
}
}
使用该传播性,会建立保存点。
当方法B的异常被捕获时,方法B的事务会回滚,而1.1和1.3操作都会被提交。
总结
在日常的开发中,我们常用的传播性有两种,REQUIRED和REQUIRED_NEW。
- REQUIRED适用于应用层编排不同业务域的服务实现同一个功能时,将每个方法都设置为REQUIRED,从而保证不同业务域的方法同时提交变动或者同时回滚。
- REQUIRED_NEW可以保证内层方法开启独立于外层方法的事务,如果内层事务方法抛出了异常,并且外层事务方法catch住了异常,并没有向外抛出,则外层事务不会回滚。如果内层事务抛出异常,并且外层事务方法没有catch掉异常,则外层事务也会回滚。适用于内层方法的执行独立于外层方法的场景,例如日志插入。
- NEVER使用场景:事务方法A内调用了非事务方法B,方法B内部作用是rpc调用远程方法,假设事务A方法开启了事务,默认情况下非事务方法B会合并到方法A的事务内执行。而我们知道像rpc这种耗时比较长的调用,实践上是不建议放到事务内执行的,因为这会导致开启的事务长期得不到释放,可能会导致数据库连接池被打满。而假设方法B上加了Never方法,则显示告诉上层调用者,不要在开启事务后调用自己。