说明:Spring的声明式事务,默认情况下只对RuntimeException非受检异常及其子类或者 Error异常及其子类才会执行回滚,而Exception受检异常是不回滚的。
分析: 大多数情况下,使用者要求对事务的回滚,不仅仅局限于运行时异常(即非受检异常) 才需要回滚,而是出现异常都进行回滚。
正例: 在开启事务时指定对Exception受检异常回滚,只要是Exception异常或者其子类异常都会回滚。
@Transactional(rollbackFor = {Exception.class})
public class CustomerServiceImpl implements CustomerService
说明:Spring的声明式事务,通过代理代理,采用切面的方式来透明的增加事务操作,如果直接在目标 对象内部调用方法,没有切面包装的,就不会通过事务调用。
分析:当获取有开启事务的Service对象时,Spring注入进来的bean并不是原实现类的对象,而是通过 继承原对象动态生成一个代理java类,并创建这个代理对象,然后依赖注入到使用者中,当使用 者调用时Service方法时,会先经过代理包装的处理逻辑,然后才调用原对象的实际逻辑。
分析: 单独调用saveCustomerBasicInfo方法或者saveCustomerWorkInfo方法,是有事务切入的, 但是this.saveCustomerWorkInfo(null, urUser);方法是没经过事务切入的。
说明:当Service调用多个Service的方法时,如果是使用同一个事务,即使调用者有try-catch捕捉了异常, 也会导致事务的回滚。
说明:mysql默认的隔离级别是Repeatable read 是保证同一事务内可重复读。导致在同一事物时, 首次进行查询后,就好对该表生成快照,导致后续每次读取同一个表的数据时都相同。 即使有存在有已提交事务的数据,也不会被读取出来。
说明:当在有开启事务的ServiceA的方法中,调用ServiceB的方法时,如果ServiceB的方法是独立的, 不受外部事务的影响,则需要给ServiceB的方法单独开启新的事务。
使用方案:
1、直接在ServiceB的方法上增加事务注解,指定事务传播方式:
@Transactional(rollbackFor = Exception.class, propagation=Propagation.REQUIRES_NEW)
public List<XdBankInfo> findRepetition(XdBankInfo xdBankInfo) {
……
}
2、包装在事务执行器里TransactionExecutor对象:
transactionExecutor.execUseNewTrans(ts -> {
….
});
附加:Spring的声明式事务在项目里非常常用,但是很多开发人员并没去了解其中的原理,事务是否生效也不知情,导致生产出现了bug后才来细查原因。最好的方式,就是做单元测试,模拟异常看数据回滚情况。