【问题标题】:Call @Transactional annotated method from another @Transactional annotated method从另一个 @Transactional 注释方法调用 @Transactional 注释方法
【发布时间】:2018-02-23 00:13:48
【问题描述】:

当我从另一个事务方法调用一个事务方法时会发生什么,现在我的第二个事务方法完成了,它在第一个事务方法中返回,不幸的是它失败了,所以它会回滚所有内容,意味着它会回滚 2nd事务方法更改..??注意:两种方法都在同一个类中

@Transactional 
public void method1(){
   //do something
   call method2();
  //do something
  ...
  ...
  failed here
}

@Transactional
public void method2(){
  //do something
  save()
}

那么在上面的例子中,它会回滚我在第二个事务方法中保存的任何内容吗?

【问题讨论】:

  • 应该只回滚 method1 中发生的事情,因为来自 method2 的事务已关闭,因此每个数据都被持久化
  • 其实没有。两种方法都在同一个类中。因此,如果他的代码使用“this.method2()”调用方法2,那么所有事务注释都将无关紧要,因为根本不会调用代理对象。并且调用必须通过代理对象,以便 Spring 可以处理带有 Transactional 注释的方法调用。

标签: java spring spring-boot transactions


【解决方案1】:

这取决于 txType。默认情况下它是必需的。所以第一个方法启动事务,同一个事务用于调用method2。

还要注意同一对象内的方法调用不会触发事务处理。通常,事务处理作为代理处理,仅在调用注入的其他 bean 时才起作用。不过,在您的示例中,您不会注意到差异。

如果method1不是@Transactional而method2是,这很重要。在这种情况下,根本就没有交易。

【讨论】:

  • “还要注意同一对象内的方法调用不会触发事务处理” - 这是否意味着方法 2 将成为方法 1 的事务的一部分?
  • 它将成为事务的一部分,因为事务在 method1 启动时开始,在 method1 结束时结束。中间所有不调整交易的调用都将参与。因此,在这种情况下,是否对 method2 进行了注释并没有什么不同。
  • 我们是否需要在 Spring Boot 中将@Transactional 添加到所有服务级别调用中,这是否有助于优化任何级别的数据库连接使用?
【解决方案2】:

如果两个方法在同一个类中,@Transactional 注释甚至不会在 calling from another method of the same class 时被考虑。不管你放什么,或者即使你把它放在外面也没关系。仍然会有一个由method1() 启动的事务,但是你被困在那个事务中。

如果第二种方法在一个单独的类中,您可以使用Propagation.REQUIRES_NEW 让它在自己的事务中运行,这意味着即使method1() 稍后失败,method2() 中的任何更改仍然会进行。

REQUIRED 的默认事务传播在不存在时启动新事务,或者加入现有事务。同样,在单独的类情况下,当 method1() 失败时,它会导致在 method2() 中所做的任何更改回滚。

【讨论】:

    【解决方案3】:

    Spring boot 使用 @Transactions 提供传播概念。传播级别决定内部事务应该是同一外部事务的一部分,或者应该是不同的独立事务。默认传播级别是REQUIRED,这意味着内部事务将是同一个外部事务的一部分,因此如果内部事务失败,整个事务将被回滚。

    现在重要的是要知道回滚默认仅适用于运行时异常。对于已检查的异常,您必须明确指定 @Transcations(rollbackFor = Exception.class)

    所以回答你的问题是肯定的!它回滚内部事务所做的更改。

    【讨论】:

      【解决方案4】:

      这是一个典型的问题:

      1. 你应该通过自注入对象调用method2()或使用this符号,因为其他方式会调用method2,避免调用代理对象方法,其中包含所有事务逻辑。

        在代理模式下(默认),只有通过代理传入的外部方法调用会被拦截。这意味着自调用,实际上是目标对象中的一个方法调用目标对象的另一个方法,在运行时不会导致实际事务,即使调用的方法被标记为@Transactional。此外,代理必须完全初始化以提供预期的行为,因此您不应在初始化代码中依赖此功能,即@PostConstruct

      2. 嵌套事务行为取决于@Transactional 参数。例如 - 使用PROPAGATION_REQUIRES_NEW 为具有@Transactional 的嵌套方法创建新事务。在official doc 中查看更多信息

      【讨论】:

        【解决方案5】:

        会发生什么取决于所选的事务传播。默认值为:“REQUIRED”,这意味着如果不存在事务,它将被启动。

        因此,在您的代码中,method2 将加入为 method1 创建的现有事务。

        至于您在同一个类中使用两种方法的情况,请注意,因为 Spring 代理对象的工作方式,这不会像您预期的那样工作。

        【讨论】:

          【解决方案6】:

          这取决于您的 transaction propagation 配置。

          相关官方文档在这里

          https://docs.spring.io/spring/docs/current/spring-framework-reference/html/transaction.html#tx-propagation

          请注意图表,传播定义了事务的“事务上下文”。

          【讨论】:

            猜你喜欢
            • 2011-09-07
            • 2021-01-01
            • 2020-03-29
            • 2012-08-25
            • 2019-11-18
            • 2015-05-27
            • 2021-04-25
            • 2014-12-25
            • 2020-02-14
            相关资源
            最近更新 更多