意外回滚异常 - 完整的场景分析

2022-09-01 20:50:41

我所知道的关于这个异常的只是来自Spring的文档和一些论坛帖子,其中霜冻的开发人员粘贴了巨大的堆栈痕迹,并且没有回复。

来自Spring的文档:

在尝试提交事务导致意外回滚时抛出

我想一劳永逸地了解

  1. 究竟是什么原因造成的?

    • 回滚发生在哪里?在应用服务器代码中还是在数据库中?
    • 它是由于特定的基础异常(例如,来自java.sql.*的东西)引起的吗?
    • 它与休眠有关吗?它是否与Spring事务管理器有关(在我的情况下不是JTA)?
  2. 如何避免?有没有避免它的最佳实践?

  3. 如何调试?它似乎很难重现,任何经过验证的方法来排除故障?

答案 1

我发现这是在回答问题的其余部分:https://jira.springsource.org/browse/SPR-3452

我想我们需要在这里区分“逻辑”交易范围和“物理”交易......

PROPAGATION_REQUIRED创建的是它所应用到的每个方法的逻辑事务范围。每个这样的逻辑事务作用域都可以单独决定仅回滚状态,外部事务作用域在逻辑上独立于内部事务作用域。当然,在标准PROPAGATION_REQUIRED行为的情况下,它们将被映射到相同的物理事务。因此,在内部事务作用域中设置的仅回滚标记确实会影响外部事务实际提交的机会。但是,由于外部事务作用域本身没有决定回滚,因此回滚(由内部事务作用域静默触发)在该级别出现意外 - 这就是引发意外回滚异常的原因。

相比之下,PROPAGATION_REQUIRES_NEW对每个受影响的事务范围使用完全独立的事务。在这种情况下,基础物理事务将有所不同,因此可以独立提交或回滚,外部事务不受内部事务回滚状态的影响。

PROPAGATION_NESTED又不同,因为它使用单个物理事务,其中包含可以回滚到的多个保存点。这种部分回滚允许内部事务作用域触发其作用域的回滚,尽管某些操作已回滚,但外部事务仍能够继续物理事务。这通常映射到JDBC保存点,因此只能与JDBC资源事务(Spring的DataSourceTransactionManager)一起使用。

为了完成讨论:意外的回滚异常也可能在应用程序本身没有设置仅回滚标记的情况下被抛出。相反,事务基础结构可能已确定,由于当前事务状态的约束,唯一可能的结果是回滚。这与 XA 事务尤其相关。

正如我上面所建议的,在内部事务范围引发异常,然后在外部范围捕获该异常并将其转换为静默 setRollbackOnly 调用,这应该适用于你的方案。外部事务的调用方将永远不会看到异常。由于您只担心调用方施加的特殊要求而导致的这种无提示回滚,我甚至认为正确的体系结构解决方案是在服务层中使用异常,并将这些异常转换为服务外观级别的静默回滚(在返回到该特殊调用方之前)。

由于您的问题可能不仅与回滚异常有关,还与从服务层引发的任何异常有关,因此您甚至可以在整个服务层中使用标准的异常驱动回滚,然后在事务已经完成后捕获并记录此类异常,在某些适应性服务外观中,将服务层的异常转换为特定于 UI 的错误状态。

于尔根


答案 2

在日志中向后滚动一点(或增加其缓冲区大小),您将看到究竟是什么导致了异常。

如果它碰巧不存在,请检查 和 方法 - 它们可能有用。getMostSpecificCause()getRootCause()UnexpectedRollbackException