记⼀次实际开发过程中遇到事务报错问题
Transactionsynchronizationi。。。
⼀:问题场景
  在⼀次http请求的后台接⼝中返回结果中出现了这个错误信息“Transaction synchronization is not active”,意思是“事务同步器没有激活”,但是被调⽤的接⼝已经添加了@Transactional注解,所以百思不得不得其解为什么还会报这个错误,那么是什么原因引起了这个异常呢?⾃⼰通过百度⼤神博客终于到⼀篇相似的⽂章解决了⾃⼰的问题,今天就是把解决⽅案记录下来为以后再出现类似情况提供解决⽅案。
⼆:出现原因
  Google搜索⼀番之后,发现了这篇⽂章Spring的TransactionEventListener,⽂中提到了可能出现这个错误信息的⼀种情况:
  @EventListener
  public void afterRegisterSendMail(MessageEvent event) {
    // Spring 4.2 之前
springframework事务    isterSynchronization(
      new TransactionSynchronizationAdapter() {
        @Override
        public void afterCommit() {
          internalSendMailNotification(event);
      }
    });
  }
  上⾯的代码将在事务提交后执⾏.如果在⾮事务context中(就是⽅法未开启spring事务)将抛出java.lang.IllegalStateException: Transaction synchronization is not active
  于是到⾃⼰代码中的TransactionSynchronizationManager部分,果然在当前http请求链路中的⼀个接⼝⾥发现了类似的代码:
  isterSynchronization(new TransactionSynchronization() {
    @Override
    public void afterCommit() {
      try {
        byte[] bytes = JSONBytes(task, new SerializeConfig());
        MessageProperties messageProperties = new MessageProperties();
        messageProperties.StringId());
        Message message = new Message(bytes, messageProperties);
        salaryFileMsgProducer.sendSalaryFileMsg(message);
      } catch (Exception e) {
        (e.getMessage());
        e.printStackTrace();
      }
    }
  });
  想必是执⾏这段代码的时候报的异常,但是上⾯那篇⽂章说了引起这个异常的原因是“在⾮事务context中注册同步器”,难道当前事务没有开启?我们的业务代码⼀般都是使⽤spring的注解@Transactional来开启事务,那么去看⼀下开启事务的代码⽚段。
三:根本原因
  我在调⽤isterSynchronization()的⽅法体上到了@Transactional注解,⽅法代码如下,省略具体实现:
  @Transactional(rollbackFor = Exception.class)
  public Boolean creatSalaryMonthStatistic(SalaryMonthlyStatistic salaryMonthlyStatistic){
    ....
  }
  这就奇怪了,明明⽅法上已经添加@Transactional注解开启事务,为什么事务开启失败,带着这个疑问再次求助了Google,发现了这篇博客spring的service类调⽤⾃⼰⽅法事务⽆效,作者也遇到了事务⽆效的问题,其中总结了事务有效性相关的⼏点重要信息:
  1.在需要事务管理的地⽅加@Transactional 注解。@Transactional 注解可以被应⽤于接⼝定义和接⼝⽅法、类定义和类的 public ⽅法上。
  2.@Transactional 注解只能应⽤到 public 可见度的⽅法上。如果你在 protected、private 或者 package-visible 的⽅法上使⽤
@Transactional 注解,它也不会报错,但是这个被注解的⽅法将不会展⽰已配置的事务设置。
  3.注意仅仅 @Transactional 注解的出现不⾜于开启事务⾏为,它仅仅是⼀种元数据。必须在配置⽂件中使⽤配置元素,才真正开启了事务⾏为。(spring配置⽂件中,开启声明式事务)
  4.通过元素的 “proxy-target-class” 属性值来控制是基于接⼝的还是基于类的代理被创建。如果 “proxy-t
arget-class” 属值被设置为“true”,那么基于类的代理将起作⽤(这时需要CGLIB库cglib.jar在CLASSPATH中)。如果 “proxy-target-class” 属值被设置为 “false” 或者这个属性被省略,那么标准的JDK基于接⼝的代理将起作⽤。
  5.Spring团队建议在具体的类(或类的⽅法)上使⽤ @Transactional 注解,⽽不要使⽤在类所要实现的任何接⼝上。在接⼝上使⽤
@Transactional 注解,只能当你设置了基于接⼝的代理时它才⽣效。因为注解是不能继承的,这就意味着如果正在使⽤基于类的代理时,那么事务的设置将不能被基于类的代理所识别,⽽且对象也将不会被事务代理所包装。
  6.@Transactional的事务开启,或者是基于接⼝的或者是基于类的代理被创建。所以在同⼀个类中⼀个⽆事务的⽅法调⽤另⼀个有事务的⽅法,事务是不会起作⽤的。
特别注意第6点:同⼀个类中⼀个⽆事务的⽅法调⽤另⼀个有事务的⽅法,事务是不会起作⽤的。这⼀点引起了我的注意,可能我的加了注解@Transactional的creatSalaryMonthStatistic⽅法也是被类⾥另⼀个没有开启事务的⽅法调⽤,如果真的是这样,所有⼀切都解释的通了。所以带着这个思路检查了⼀遍⾃⼰的代码,
  果然,creatSalaryMonthStatistic⽅法被⼀个没加事务的接⼝⽅法调⽤,从⽽整个creatSalaryMonthStatistic⽅法都没有事务效果:
  //此接⼝⽅法未加事务
  public Boolean batchInitSalaryFileDataTask4Tenant(String tenantId){
    .....
    ateSalaryFileDateInitTask4Staff(file);
    .....
  }
四:解决⽅案
  知道了引起问题的原因,解决⽅法也⽐较简单,有好⼏种⽅法都可以解决这个问题,可以根据个⼈需要选择⼀种解决⽅法,本⽂列出其中三种:
  1.⽐较⽅便、暴⼒的⼀种⽅法就是直接在最外层接⼝⽅法中添加事务注解,也就是给本⽂中的batchInit
SalaryFileDataTask4Tenant⽅法加上注解即可。
  2.也可以通过代理类调⽤creatSalaryMonthStatistic⽅法,代理类中包含了事务逻辑,这样也能实现事务功能。
  3.还可以直接将creatSalaryMonthStatistic这个⽅法放到另外⼀个类中。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。