【问题标题】:Spring transactions with caught Exceptions捕获异常的 Spring 事务
【发布时间】:2015-02-27 00:56:11
【问题描述】:

最近,我一直在使用 Spring boot + spring data jpa + hibernate。我在春季交易中遇到了一个问题。这是我的服务类和两个问题:

@Transactional
@Service
class MyService {

    @Autowired
    private MyRepository myRep;

    public void method_A() {
       try {
          method_C();

          .....

          method_B();
       } catch(Exception e) {}
    }

    public void method_B() {
       Entity e = new Entity();
       e.set(...);

       myRep.save(e);
    }

    public void method_C() throws Exception {
       ....
    }
}

1.如果method_C()方法抛出异常,我想捕获它并记录它,事务在method_B()方法中没有回滚,因为异常没有到达Spring框架。那么我应该怎么做才能从method_C() 捕获异常,同时不失去方法method_B() 被回滚的能力?

2.考虑新方法method_A()

public void method_A() {
   for(...) {
      ...
      ...
      method_B();
   }
}

我想循环调用method_B()。如果method_B() 中发生异常,我希望method_B() 的事务被回滚,但method_A() 不应该退出并且循环应该继续执行。我怎样才能做到这一点?

【问题讨论】:

  • 不要捕获异常,spring 无论如何都会记录异常,包括完整的堆栈跟踪,所以为什么还要自己做呢。对于选项 2,只需在 for 循环中放置一个 try/catch。方法 B 仍然按原样运行(包括回滚),方法 a 仍然继续。如果您想要一些额外的异常处理,您总是可以编写一个 after throwing 方面来进行日志记录,或者添加一个 catch 但总是重新抛出异常(但由于 Spring 已经记录了这将是一个但多余的)。
  • 如果我在循环中添加try-catch 块,事务不会在method_B() 中回滚。实体被保存并提交到数据库。
  • 这里的假设是methodA不在事务中,只有method B。
  • 你的意思是如果只在方法method_B()之前写@Transactional它应该表现得像你说的那样?
  • 不,因为那将是一个内部方法调用,并且因为 Spring 默认使用代理,所以根本没有事务。当您使用提前期或编译时编织时,您可以做到这一点。

标签: java spring hibernate jpa spring-data


【解决方案1】:

我通过这种方式解决了我的 2 个问题:创建了另一个 @Service 类并将 method_B() 移入其中。我已将此类注释为@Transactional。现在method_A() 方法看起来像这样:

public void method_A() {
   for(...) {
      ...
      try {
         anotherService.method_B();
      } catch (Exception e) {
         logger.error(...);
      }
   }
}

如果RuntimeException 出现在method_B() 方法中,异常会被正确记录,method_B() 的事务被回滚并且循环继续。谢谢大家的回复。

【讨论】:

    【解决方案2】:

    不要抛出异常,请执行以下操作。 (返回错误代码)。

    更新:我在发布后阅读了您的问题。如果您从 method_A 调用 method_b 两者都在同一事务下。不幸的是,您不能单独回滚 method_b 更改。如果它们都在一个服务类下,Spring 将其视为一个事务。 (所有方法)。

    您可以尝试以下方法。

    request to--> Controller() ---> (spring opens transaction) service_method_a(); (spring closes transaction) 
                   Controller() ---> (spring opens transaction) service_method_c(); (spring closes transaction)  
                   Controller() ---> (spring opens transaction) service_method_b(); (spring closes transaction) 
    return <--
    

    我希望它有意义

    如果想要回滚,您的每个方法 a、b、c 都会抛出异常。

    更新: 另一种方法。这个好多了。

    如果您的每个方法都在不同的服务中,那么您可以使用 spring 的以下注释在不同的事务边界中运行每个方法

    p v serviceA{
    
       @transactional
       method_a(){
           serviceb.method_b();
       }
    
    }
    
    p v serviceB{
    
       @Transactional(propagation=Propagation.REQUIRED)
       method_b(){
    
       }
    
    }
    

    更多信息here

    春季交易故事here。阅读本文下方的要点。在开发 Spring 事务应用程序时,这些是最重要的。

    【讨论】:

    • 宙斯,感谢您的回复,但method_B() 不会在出现异常时回滚,因为您已经用 try-catch 块包围了它的主体。
    • 值得一试,谢谢。另外,我想问一下您的声明“如果您从 method_A 调用 method_b 两者都在同一个事务下。不幸的是,您不能单独回滚 method_b 更改。如果它们都在一个服务类下,Spring 将其视为一个事务。”如果这些方法在同一个事务下,那么为什么我们可以在具有不同参数(传播,隔离等)的方法(不是类)之前添加@Transactional注解?是不是因为我们希望不同方法的交易表现不同?
    • @polis 我已经更新了答案。请参阅最后的链接,以了解我要说的内容。它已经在某个地方得到了回答,所以,我不会再解释它了。
    猜你喜欢
    • 1970-01-01
    • 2011-10-27
    • 2016-11-25
    • 2015-03-02
    • 1970-01-01
    • 2017-01-12
    • 2012-03-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多