一、事务的传播特性
spring的事务传播行为通俗来讲是事务和事务之间的关系,比如serviceA#methodA()和serviceB#methodB()这两个方法定义了不同的事务,那么事务是事务之间嵌套使用的时候要用什么样的行为呢?这个就是spring替我们控制的事务传播行为,从枚举Propagation中我们可以看到一共有七种,分别是
1.REQUIRED(如果当前session没有事务就创建事务);
2.SUPPORTS(如果当前session有事务就用,没有就不用,取决于调用方);3.MANDATORY(如果当前session没有事务就报错);
4.REQUIRES_NEW(将调用方session挂起,重新拿出一个session并开启事务,所以和调用方的事务是没有关系的,内层的事务提交过程中报错,外层事务肯定会回滚,但是如果内层事务已经提交,外层事务再报错,那么内层事务是不会回滚的);
5.NOT_SUPPORTED(不支持事务,如果session有事务就挂起事务),这种情况可以在查询的时候用;
6.NEVER(不支持事务,如果session有事务那么就报错);
7.NESTED(这个是事务嵌套,它和REQUIRES_NEW非常相似,但是两者是不一样的,区别在于NESTED它必须通过外层事务来完成提交的动作,外层事务的回滚也会造成内部事务的回滚。但是REQUIRES_NEW如果内部事务提交之后外部事务出现异常回滚的时候内部事务是不会回滚的);
其实除了NESTED,其他的6个在EJB中都有类似的,而NESTED是spring提供的,而且要求JDBC driver.在3.0以上
二、事务的隔离级别
起初学习事务的时候一直没弄明白事务的传播行为和事务的隔离级别,目前理解两个并没有必然联系,事务的隔离级别不同数据库有不同的定义,而且默认的隔离级别并不是一样的,它定义的是事务在什么时候提交,而事务的传播特性定义的是多个带有事务的方法之间调用的时候,事务之间的传播关系。
用代码来说如下
//先看两个接口的定义
public interface IServiceA {
public void insertBoth(LeaveRule leaveRule, LeaveRuleScope leaveRuleScope);
}
public interface IServiceB {
public void updateBoth(LeaveRule leaveRule, LeaveRuleScope leaveRuleScope);
}
//两个接口的实现,其中A方法中调用了B
@Service
public class ServiceAImpl implements IServiceA{
@Autowired
private ILeaveRuleDao leaveRuleDao;
@Autowired
private ILeaveRuleScopeDao leaveRuleScopeDao;
@Autowired
private IServiceB serviceB;
//Propagation.REQUIRED 没有事务就新建事务,默认就是这个
@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
@Override
public void insertBoth(LeaveRule leaveRule, LeaveRuleScope leaveRuleScope) {
leaveRuleDao.update(leaveRule);
leaveRuleScopeDao.insert(leaveRuleScope);
serviceB.updateBoth(leaveRule,leaveRuleScope);
throw new RuntimeExcetion();
}
}
下面是B接口的实现,实现方法定义了传播行为是REQUIRES_NEW,我们要测试的内容见注释
@Service
public class ServiceBImpl implements IServiceB {
@Autowired
private ILeaveRuleDao leaveRuleDao;
@Autowired
private ILeaveRuleScopeDao leaveRuleScopeDao;
//REQUIRES_NEW 不管调用方serviceA.saveBoth();有没有事务,调用updateBoth()的时候都会新建事务
//而且无论serviceA.saveBoth()成功与否,都不影响本事务的提交,而且本事务是否提交也不会影响A事务
//可以理解为A和B就是两个不相关的事务
@Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor = Exception.class)
@Override
public void updateBoth(LeaveRule leaveRule, LeaveRuleScope leaveRuleScope) {
leaveRuleDao.update(leaveRule);
leaveRuleScopeDao.insert(leaveRuleScope);
}
}
这里直接说测试结果:
1.如果内层事务ServiceBImpl#updateBoth()发生了异常,无论他的内外层设置的事务传播行为是什么,外层事务都会回滚。
2.如果内层事务ServiceBImpl#updateBoth()没有发生异常,并且定义的传播行为是REQUIRES_NEW,这个时候外层事务ServiceAImpl#insertBoth()在内层事务提交之后发生了异常,那么只会外层事务回滚,内层事务仍然会提交。
3.如果内层事务ServiceBImpl#updateBoth()没有发生异常,但是定义的传播行为是NESTED,也就是事务嵌套,那么如果外部事务出现异常提交不成功,内部事务也会提交失败,它是随着外部事务提交或回滚的。