UnexpectedRollbackException - 完整的场景分析

时间:2010-01-05 15:42:47

标签: java hibernate spring exception

我所知道的关于这个异常的全部内容来自Spring的documentation以及一些论坛帖子,其中有霜冻的开发人员粘贴了大量的堆栈跟踪,没有回复。

来自Spring的文档:

  

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

我想一劳永逸地理解

  1. 究竟是什么导致了它?

    • 回滚发生在哪里?在App Server代码中还是在数据库中?
    • 是由于特定的底层异常引起的(例如来自java.sql。*的东西)?
    • 它与Hibernate有关吗?它与Spring Transaction Manager(在我的情况下是非JTA)有关吗?
  2. 如何避免?有什么最佳做法可以避免吗?

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

3 个答案:

答案 0 :(得分:11)

我发现这是为了回答其余问题:https://jira.springsource.org/browse/SPR-3452

  

我想我们需要区分   在“逻辑”事务范围之间   和'实体'交易在这里...

     

PROPAGATION_REQUIRED创建的是一个   每个的逻辑事务范围   它被应用到的方法。每   这样的逻辑事务范围可以   单独决定仅回滚   状态,具有外部事务   范围在逻辑上独立于   内部交易范围。的   当然,如果是标准的话   他们是PROPAGATION_REQUIRED行为   将被映射到相同的物理   交易。所以只回滚标记   在内部事务范围中设置   确实会影响外部交易   实际承诺的机会。然而,   因为外部交易范围确实如此   没有决定回滚本身,   回滚(由...静默触发)   内部交易范围)来了   在那个级别意外 - 这是   为什么是UnexpectedRollbackException   被抛出。

     

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

     

PROPAGATION_NESTED再次不同   因为它使用单个物理   具有多个保存点的事务   它可以回滚。这样偏   回滚允许内部事务   触发其回滚的范围   范围,与外部交易   能够继续物理   交易尽管有一些操作   已被退回。这是   通常映射到JDBC保存点,   所以只能用于JDBC资源   交易(春天的   DataSourceTransactionManager对象)。

     

完成讨论:   UnexpectedRollbackException也可能   在没有应用程序的情况下抛出   设置了仅回滚标记   本身。相反,交易   基础设施可能已经决定了   唯一可能的结果是   回滚,由于受到限制   当前的交易状态。这是   与XA特别相关   交易。

     

正如我上面提到的,抛出一个   内部交易的例外情况   范围,然后捕获该异常   外部范围并翻译它   进入一个静默的setRollbackOnly调用   应该适用于您的场景。一个   外部交易的来电者会   从来没有看到过例外。自从你   只担心这种无声回滚   因为有特殊要求   由来电者强加,我甚至会   认为正确的建筑   解决方案是使用内部异常   服务层,并进行翻译   这些异常进入静默回滚   在服务门面层面(右边   在回到那个特殊之前   呼叫者)。

     

因为你的问题可能不是   仅关于回滚异常,但是   而是抛出任何异常   从你的服务层,你可以   甚至使用标准异常驱动   在你的整个过程中回滚   服务层,然后捕获并记录   交易后的此类例外情况   在某些方面已经完成了   适应服务门面   翻译您的服务层   特定于UI的错误   的状态。

     

尔根

答案 1 :(得分:8)

在日志中再滚动一点(或增加它的缓冲区大小),你会看到究竟是什么导致了异常。

如果碰巧不在那里,请检查getMostSpecificCause()的{​​{1}}和getRootCause()方法 - 它们可能有用。

答案 2 :(得分:4)

我可以告诉你如何重现UnexpectedRollbackException。我正在处理我的项目,在以下情况下我得到了这个UnexpectedRollbackException。我的项目中有控制器,服务和dao层。 我所做的是在我的服务层类中,

class SomeServiceClass {
    void outerTransaction() {
        // some lines of code
        innerTransaction();
        //AbstractPlatformTransactionManager tries to commit but UnexpectedRollbackException is thrown, not here but in controller layer class that uses SomeServiceClass.
    }

    void innerTransaction() {
        try {
            // someDaoMethod or someDaoOperation that throws exception
        } catch(Exception e) {
            // when exception is caught, transaction is rolled back, outer transaction does not know about it.
            // I got this point where inner transaction gets rolled back when I set HibernateTransactionManager.setFailEarlyOnGlobalRollbackOnly(true)
            // FYI : use of following second dao access is wrong, 
            try {
                // again some dao operation
            } catch(Exception e1) {
                throw e2;
            }
        }
    }
}