处理事务回滚

处理事务回滚

参考文献:《极客时间-Java业务开发常见错误100例》https://time.geekbang.org/column/article/213295

大多数Spring Boot项目只需要在方法上标记@Transactional注解,即可一键开启方法的事务性配置。

保证事务生效

  1. 务必确认调用 @Transactional 注解标记的方法是 public 的

    除非特殊配置(比如使用 AspectJ 静态织入实现 AOP),否则只有定义在 public 方法上的 @Transactional 才能生效。原因是,Spring 默认通过动态代理的方式实现 AOP,对目标方法进行增强,private 方法无法代理到,Spring 自然也无法动态增强事务处理逻辑。
    
    如果要针对 private 方法启用事务,动态代理方式的 AOP 不可行,需要使用静态织入方式的 AOP,也就是在编译期间织入事务增强代码,可以配置 Spring 框架使用 AspectJ 来实现 AOP。你能否参阅 Spring 的文档“Using @Transactional with AspectJ”试试呢?注意:AspectJ 配合 lombok 使用,还可能会踩一些坑。 https://docs.spring.io/spring/docs/current/spring-framework-reference/data-access.html#transaction-declarative-aspectj 
    
  2. 通过 Spring 注入的 Bean 进行调用的。使用 try…catch…来包裹标记了 @Transactional 注解的方法,必须通过代理过的类从外部调用目标方法才能生效。

    Spring 通过 AOP 技术对方法进行增强,要调用增强过的方法必然是调用代理后的对象。
    - CGLIB 通过继承方式实现代理类,private 方法在子类不可见,自然也就无法进行事务增强;
    - this 指针代表对象自己,Spring 不可能注入 this,所以通过 this 访问方法必然不是代理。
    
    正确使用:在 Service 内部注入自己调用自己的 方法() 可以正确实现事务,但更合理的实现方式是,让 Controller 直接调用之前定义的 Service 的 方法() ,因为注入自己调用自己很奇怪,也不符合分层实现的规范
    

image

保证回滚

默认情况下,出现 RuntimeException(非受检异常)或 Error 的时候,Spring 才会回滚事务。

修复方式:

  1. 自己捕捉,手动设置让当前事务处于回滚状态

    @Transactionalpublic void createUserRight1(String name) {
        try {
            userRepository.save(new UserEntity(name));
            throw new RuntimeException("error");
        } catch (Exception ex) {
            log.error("create user failed", ex); 
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
    }
    
  2. 在注解中声明rollbackFor ,期望遇到所有的 Exception 都回滚事务(来突破默认不回滚受检异常的限制)

    @Transactional(rollbackFor = Exception.class)
    

子方法回滚,主方法不回滚

虽然捕获了子方法的异常,但是因为没有开启新事务,而当前事务因为异常已经被标记为rollback了,所以最终还是会回滚。

解决方法:想办法让子逻辑在独立事务中运行

  • 为注解加上 propagation = Propagation.REQUIRES_NEW 来设置 REQUIRES_NEW 方式的事务传播策略,也就是执行到这个方法时需要开启新的事务,并挂起当前事务。

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    

    主方法没什么变化,同样需要try catch捕获异常,防止异常漏出去导致主事务回滚

posted @ 2020-06-30 16:38  shinl00  阅读(709)  评论(0编辑  收藏  举报