spring事务的传播行为详细理解
一,spring七中事务传播行为
1、 REQUIRED:默认的传播特性,业务方法需要在一个事务中运行,如果一个方法已经处在一个事务中那么就加入到这个事务中,否则就会创建一个事务。
2、 NEVER:指定的业务方法绝对不能在事务范围内运行,如果业务方法在某个事务中执行,就会抛异常,只有业务方法没有任何事务才正常执行。
3、 MANDATORY:该属性指定业务方法只能在一个已经存在的事务中执行,业务方法不能自己发起自己的事务,如果业务方法不存在事务,容器就抛异常。
4、 SUPPORTS:如果业务方法中已经在某个事务中被调用,则方法就称为事务的一部分,如果外部业务方法没有开启事务,supports该方法也会在没有事务的环境中执行。
5、 NOT_SUPPORTED:如果该业务方法在一个事务中被调用,那么当前的事务会被挂起,执行该业务方法,方法执行完毕唤醒被挂起的事务,如果业务方法不在一个事务中执行,该方法也不会开事务。不管是否在有无事务的环境中执行都不开启事务。
6、 REQUIRES_NEW:不管是否存在事务,业务方法总会自己开启一个事务,如果在已有事务的环境中调用,已有事务会被挂起,新的事务会被创建,直到业务方法调用结束,已有事务才被唤醒
7、 NESTED:如果业务方法在一个事务中执行,就在这个事务中嵌套,如果没有事务按着required执行,开启单独的事务,这种事务有多个事务的保存点,NESTED内部事务异常会回滚 外部事务不会回滚(前提条件,外部事务将内部事务的异常捕获了,不捕获的话,两个事务都会回滚) ;内部事务没有异常,外部事务有异常 则整体事务都回滚
二、传播行为使用场景和结论
前提有两个方法,两个方法中都使用到了事务,@Transactional默认传播行为为REQUIRED
比如
A方法
@Transactional public void A() { UserInfo userInfo = new UserInfo(); userInfo.setUserId(797220166600626176L); userInfo.setPassWord("laoda"); userInfoMapper.updateByPrimaryKeySelective(userInfo); try { userLoginService.B(); // 这里需要userLoginService.B(),不能使用B(),因为A方法和B方法在一个service中,B()方法的事务会不起作用,后续分析事务失效问题 }catch (Exception e) { log.info("我捕获了异常,看你能不能回滚"); } int x = 1; if(x == 1) { throw new RuntimeException(); } }
B方法
@Transactional public void B() { UserInfo userInfo = new UserInfo(); userInfo.setUserId(797221588868141056L); userInfo.setPassWord("xiaodi"); userInfoMapper.updateByPrimaryKeySelective(userInfo); int x = 0; if(x == 1) { throw new RuntimeException(); } }
场景一 A方法和B方法都使用 propagation = Propagation.REQUIRED
@Transactional(propagation = Propagation.REQUIRED)
public void A() {
UserInfo userInfo = new UserInfo();
userInfo.setUserId(797220166600626176L);
userInfo.setPassWord("laoda");
userInfoMapper.updateByPrimaryKeySelective(userInfo);
try {
userLoginService.B();
}catch (Exception e) {
log.info("我捕获了异常,看你能不能回滚");
}
int x = 0;
if(x == 1) {
throw new RuntimeException();
}
}
@Transactional(propagation = Propagation.REQUIRED)
public void B() {
UserInfo userInfo = new UserInfo();
userInfo.setUserId(797221588868141056L);
userInfo.setPassWord("xiaodi");
userInfoMapper.updateByPrimaryKeySelective(userInfo);
int x = 0;
if(x == 1) {
throw new RuntimeException();
}
}
根据不同的情况实验结论:
若两个方法都没有出现异常,则会提交成功;
A方法将b的异常捕获,若方法B出现了异常,A方法执行后会出现Transaction rolled back because it has been marked as rollback-only, 此异常出现因为方法A和方法B共用一个事务,B方法出现了异常,将事务标记为回滚,但是异常被方法A捕获了,方法A执行完要提交的时候,发现异常被标记为回滚,所以会出现此异常(事务不会提交,数据不会更新)。解决方法不要在捕获B方法的异常或者捕获后在抛出也可以(方法A和B属于同一业务逻辑)B方法使用propagation = Propagation.REQUIRES_NEW ,B方法出现异常不会影响A方法(方法A和B不属于同一业务逻辑)。
若方法A出现异常,事务都会回滚,或者若B方法出现异常并且在方法A中不使用trycatch,事务会回滚。
场景二 A方法使用 propagation = Propagation.REQUIRED, B方法使用propagation = Propagation.REQUIRES_NEW
@Transactional(propagation = Propagation.REQUIRED)
public void A() {
UserInfo userInfo = new UserInfo();
userInfo.setUserId(797220166600626176L);
userInfo.setPassWord("laoda");
userInfoMapper.updateByPrimaryKeySelective(userInfo);
// userLoginService.B();
try {
userLoginService.B();
}catch (Exception e) {
log.info("我捕获了异常,看你能不能回滚");
}
// int x = 1;
// if(x == 1) {
// throw new RuntimeException();
// }
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void B() {
UserInfo userInfo = new UserInfo();
userInfo.setUserId(797221588868141056L);
userInfo.setPassWord("xiaodi");
userInfoMapper.updateByPrimaryKeySelective(userInfo);
// int x = 1;
// if(x == 1) {
// throw new RuntimeException();
// }
}
根据不同的情况实验结论:
若两个方法都没有出现异常,则会提交成功;
在A方法将B的异常捕获,若B方法出现异常会回滚,A方法正常提交成功;若B方法没有出现异常,事务成功提交,A方法出现异常,则A方法回滚,B方法不会回滚
在A方法中不捕获B的异常,若A出现异常 A回滚,不会影响B,B没有出现异常,B成功提交;若B出现异常。两个都会回滚
场景三、 A方法使用 propagation = Propagation.REQUIRED, B方法使用propagation = Propagation.NESTED
@Transactional(propagation = Propagation.REQUIRED)
public void A() {
UserInfo userInfo = new UserInfo();
userInfo.setUserId(797220166600626176L);
userInfo.setPassWord("laoda");
userInfoMapper.updateByPrimaryKeySelective(userInfo);
// userLoginService.B();
try {
userLoginService.B();
}catch (Exception e) {
log.info("我捕获了异常,看你能不能回滚");
}
int x = 1;
if(x == 1) {
throw new RuntimeException();
}
}
@Transactional(propagation = Propagation.NESTED)
public void B() {
UserInfo userInfo = new UserInfo();
userInfo.setUserId(797221588868141056L);
userInfo.setPassWord("xiaodi");
userInfoMapper.updateByPrimaryKeySelective(userInfo);
// int x = 1;
// if(x == 1) {
// throw new RuntimeException();
// }
}
根据不同的情况实验结论:
若两个方法都没有出现异常,则会提交成功;
在A方法中将B的异常捕获,若B方法出现异常,B会回滚,A正常提交;若A方法出现异常,则会影响B方法,两个都会回滚
在A方法中不捕获B的异常,若B方法出现异常,A和B都会回滚;若A方法出现异常,则会影响B方法,两个都会回滚