spring事务的原理是AOP,进行了切面增强,那么失效的根本原因是这个AOP不起作用了,常见情况如下:

  1. 抛出检查异常导致事务不能正确回滚
    spring事务默认只对非检查异常进行回滚,即运行时异常。检查异常就是程序编译时必须处理的。
    解决办法:可以通过配置@Transcational的属性rollbackFor来解决,将rollbackFor设置为Exception.class,即对所以异常都进行回滚。
  2. 业务方法内通过try-catch将异常捕获后没有再抛出,导致spring事务通知不能捕捉到异常,也就不能进行后面的回滚操作。
    解决办法:
  • 在catch中继续将异常抛出。
  • 在catch中手动设置事务回滚
    TransactionInterceptor.currentTransactionStatus().setRollbackOnly();
  1. aop切面顺序导致事务不能回滚
    正常情况事务切面的优先级是最低的,是包围在目标方法外的。
    自定义切面的优先级也是最低的,和事务切面一样,如果没有指定自定义切面的顺序,那么它会在事务切面的内层,它包围着目标方法,这样目标方法抛出异常后首先被自定义切面捕获到,外层的事务切面的不知道的,如果自定义切面捕获到异常后没有继续抛出,那么事务就不能正确回滚。
    解决办法:同情况2
  2. 非public方法导致的事务失效
    @Transcational注解加在public方法上才会生效
    Spring为方法创建代理、添加事务通知,前提条件都是该方法是public的
  3. 调用本类方法导致传播行为失效
    调用本类方法是不经过代理的,因此无法增强
    解决办法:
  • 依赖注入自己(代理)来调用
  • 通过AppContext拿到代理对象来调用
  1. @Transcational方法导致的synchronized失效
    synchronized保证的仅是目标方法的原子性,环绕目标方法的还有commit等操作,它们并未处一起sync块内
    解决办法:synchronized范围扩大至代理方法调用
    使用select ... fro update 替换 select
  2. @Transcational没有保证原子行为
    事务的原子性仅涵盖insert,update,delete,select ... for update语句,select方法并不阻塞