【问题标题】:Why are the transactions rolled back even when propagation=Propagation.REQUIRES_NEW in second method in Spring service class?为什么即使在 Spring 服务类的第二个方法中传播 = Propagation.REQUIRES_NEW 时事务也会回滚?
【发布时间】:2013-02-25 03:41:01
【问题描述】:

现在基本设置都很好,我开始尝试交易。 Struts+Spring+Hibernate注解事务管理器。 这是Action中的示例代码,会调用一个服务类:

    userService.addUser();

这是服务类中的addUser() 方法:

    @Transactional(value="deu"  )
    public void addUser() {     
        userDao.addUser();
        this.addUser2();

    }

首先,我在 userDao 中调用了addUser,它会插入一个用户。其次,我在这个服务类的另一个方法中调用了addUser2

    @Transactional(value="deu" , propagation=Propagation.REQUIRES_NEW  )
    public void addUser2()   {      
    //should be a new transaction and will not affect the previous one. 
            //this one will fail but should not affect the previous one.
        userDao.addUserFail();        
    }

而这个会因为 PK 无效而失败。我想第二个电话(addUser2)会失败,但不会影响前一个。但是,没有插入用户。

如果我只打电话:

   @Transactional(value="deu"  )
    public void addUser() {     
        userDao.addUser();
        //this.addUser2();      
    }

它正在工作,这意味着像数据库这样的基本设置没有错。

有什么想法吗?

【问题讨论】:

    标签: java spring hibernate dao transactionmanager


    【解决方案1】:

    Spring 的声明式事务处理使用 AOP 代理工作。当你得到一个事务性 bean 时,实际上你得到了一个代理,它包装你的 bean 实例,拦截方法调用,必要时启动事务,然后调用实际 bean 的方法,然后在必要时提交或回滚事务。

    但是你从同一个 bean 中的另一个方法调用你的 bean 的一个方法,所以代理被绕过了,并且不能应用任何事务行为。

    将方法放在另一个 bean 中,或者使用 AspectJ,它检测字节码并可以拦截 bean 内的方法调用。

    更详细的解释见Spring documentation

    【讨论】:

    • 嗨,JB:它仍然无法正常工作。我在 userService 中注入了另一个服务(countryService),在 addUser 方法中,我调用了 dao.addUser(这个将插入一个用户),然后是 countryService.addCountry(这个是propagation=Propagation.REQUIRES_NEW,并且由于空PK而失败)。但是,第一个添加的用户也会被回滚……我现在不知道为什么了~
    • 嗨,JB Nizet:我犯了你写的关于 requires_new 的另一篇文章,我想我误解了 Spring 中的某些内容。我可以再问一下,如果内部事务回滚并将其异常抛出给外部事务,外部事务是否也回滚?(如果外部不做任何事情)如果是这样,如果内部被更改,则需要并且这次再次抛出异常和外部抓住它不做任何事情,这两种情况是否意味着同一件事?如果这是真的,那么我声明的没有用......它只取决于我的程序逻辑! :(
    • 我在答案中写下了我的发现,希望是对的,请再看一遍,谢谢!
    • 你说得对。如果第二个服务使用 REQUIRED,则两个服务都在同一个事务中,如果第二个(或第一个)服务抛出异常,该事务将被标记为回滚。在第一个服务中捕获异常不会改变任何事情:事务已被标记为回滚。如果第二个服务使用 REQUIRES_NEW 并抛出异常,则它有自己的事务,该事务将被回滚。但是,与任何其他异常一样,该异常将传播到调用者(第一个服务)。所以如果它没有捕捉到它,它的事务也会被回滚。
    【解决方案2】:

    我做了一些测试,找到了结论。

    1. 如果需要第二个服务(内部)并抛出异常,即使第一个事务捕获它,它们都会回滚(因为它们在同一条船上!)

    2. 1234563触发外部回滚(即使它不是他的异常,但它是一个异常!)。所以外部必须为这种情况做点什么(设置回滚或捕获它)。

    对吗?

    【讨论】:

      【解决方案3】:

      这是因为 Spring AOP 架构。

      Spring AOP 使用代理,所以方面只是在代理上调用方法时执行。

      调用this.addUser2(...) 时,您调用的是self 对象上的方法,而不是代理。所以没有执行任何方面,也没有进行 TX 管理。

      你可以做以下事情:

      • addUser2 方法移动到另一个bean(例如UserService2),然后将新bean 注入UserService 并使用userService2.addUser2() 调用该方法。
      • UserService注入UserService(我不确定是否可以这样做),并使用userService.addUser2()而不是this.addUser2()调用addUser2()

      【讨论】:

      • 您的解决方案应该可以工作,但是,另一个被声明为 Propagation.REQUIRES_NEW 的服务(现在已经注入)在第一次调用时回滚我插入的用户。
      【解决方案4】:

      正如 Amir Pashazadeh 所说,他不知道如何在同一个 bean 中使用事务上下文调用代理,这是一个示例:

      @Component
      public class UserService(){     
      
          @Autowired @Setter private ApplicationContext  applicationContext;
          @Autowired @Setter private UserDao             userDao;
      
          @Transactional(value="deu"  )
          public void addUser() {     
      
              userDao.addUser();
              try{
                 getProxy().addUser2();
              catch(Exception ex){
                 // Avoid rolling back main transaction
                 log("OMG it failed!!")
              }
          }
      
          @Transactional(value="deu" , propagation=Propagation.REQUIRES_NEW  )
          public void addUser2()   {      
          //should be a new transaction and will not affect the previous one. 
                  //this one will fail but should not affect the previous one.
              userDao.addUserFail();        
          }
      
          private UserService getProxy() {
              return applicationContext.getBean(UserService.class); 
          }
      }
      

      请注意,如果您在 addUser2 中捕获异常但事务已标记为回滚,则 Spring 似乎会抛出 UnexpectedRollbackException

      【讨论】:

        猜你喜欢
        • 2012-09-25
        • 2020-09-30
        • 2017-03-29
        • 1970-01-01
        • 2020-05-01
        • 1970-01-01
        • 2015-03-12
        • 2019-02-09
        • 2021-02-28
        相关资源
        最近更新 更多