【问题标题】:UnexpectedRollbackException overrides my own exceptionUnexpectedRollbackException 覆盖了我自己的异常
【发布时间】:2011-03-15 19:38:47
【问题描述】:

我在spring的事务管理中有以下奇怪的场景:

我有方法 A 调用方法 B 调用方法 C,它们每个都在不同的类中。方法 B 和 C 都包含事务。两者都使用 PROPAGATION_REQUIRED,因此当 spring 创建两个逻辑事务时,db 中有一个物理事务。

现在,在方法 C 中,我抛出了一个 RuntimeException。这将内部逻辑事务设置为 rollbackOnly 和物理事务。在方法 B 中,我知道 UnexpectedRollbackException 的可能性,所以我没有继续正常提交。我从 C 中捕获异常,然后抛出另一个 RuntimeException。

我希望外部 RuntimeException 会导致回滚到外部事务,但是实际行为是这样的:

  • 外部事务似乎尝试提交,或至少检查其状态,然后它抛出 UnexpectedRollbackException,因为物理事务已被标记为 rollbackOnly。
  • 在抛出该异常之前,它会在日志中打印另一个异常,指出“应用程序异常被提交异常覆盖”。因此,调用者 A 收到 UnexpectedRollbackException,而不是 B 抛出的异常。

我找到了一个解决方法,就是在抛出异常之前主动将外部事务设置为回滚

public ModelAndView methodB(HttpServletRequest req, HttpServletResponse resp) {
  try{
    other.methodC();
  } catch (RuntimeException e){
    TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
    throw new RuntimeException ("outer exception");
  }
  return handleGetRequest(req, resp);
}

但是,这种解决方法将代码与事务 api 紧密结合在一起,我想避免这种情况。有什么建议吗?

附言 这两个事务都是为了回滚运行时异常。我没有定义任何 rollbackFor 异常或类似的东西

【问题讨论】:

    标签: java spring transactions


    【解决方案1】:

    您可以尝试在您的AbstractPlatformTransactionManager 继承者(例如HibernateTransactionManager)中将failEarlyOnGlobalRollbackOnly 标志设置为true。这是它的描述:

    设置在事务被全局标记为仅回滚的情况下是否提前失败。

    默认为“false”,只会在最外层事务边界处引发 UnexpectedRollbackException。早在第一次检测到全局仅回滚标记时,即使在内部事务边界内,打开此标志也会导致 UnexpectedRollbackException。请注意,从 Spring 2.0 开始,全局回滚标记的早期失败行为已经统一:默认情况下,所有事务管理器只会在最外层事务边界处引发 UnexpectedRollbackException。例如,即使在操作失败并且事务永远不会完成之后,这也允许继续进行单元测试。如果此标志已明确设置为“true”,所有事务管理器只会提前失败。

    【讨论】:

    • +1 感谢您的努力。 failEarlyOnGlobalRollbackOnly 很有趣,但我最终找到了问题的原因(见下面我的回答)
    【解决方案2】:

    我找到了这个问题的原因。事实证明,methodB 在被包裹在事务中之前是用基于 cglib 的代理(使用 spring 旧方式,pre 2.0)包裹的。所以当我从 methodB 抛出一个 RuntimeException 时,cglib 最终会抛出一个 InvocationTargetException,这实际上是一个检查异常。

    Spring 的事务管理器最终捕获了已检查的异常并尝试提交事务,而没有意识到方法 B 抛出的嵌套运行时异常。发现这一点后,我将事务包装器设置为回滚检查的异常,现在它按预期工作。

    【讨论】:

    • 我遇到了同样的问题。我们可以回滚所有已检查和未检查的异常吗?这样会有什么问题吗?
    猜你喜欢
    • 2014-11-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多