我仅从Spring的文档和一些论坛上发来的关于该异常的信息,其中饱受摧残的开发人员粘贴了巨大的堆栈跟踪信息,但没有回复。
从Spring的文档中:
尝试提交事务导致意外回滚时抛出
我想一劳永逸
究竟是什么原因造成的?
回滚发生在哪里?在App Server代码中还是在数据库中?
它与Hibernate有关吗?它与Spring Transaction Manager(在我的情况下不是JTA)有关吗?
如何避免呢?有什么最佳实践可以避免吗?
如何调试呢?它似乎很难复制,是否有任何成熟的方法可以对其进行故障排除?
向后滚动一点(或增加它的缓冲区大小),你将看到导致异常的确切原因。
如果碰巧不存在,请检查getMostSpecificCause()和的getRootCause()方法UnexpectedRollbackException-它们可能有用。
getMostSpecificCause()
getRootCause()
UnexpectedRollbackException
我想我们需要在这里区分”logical”交易范围和”physical”交易…
PROPAGATION_REQUIRED创建的是适用于每种方法的逻辑事务范围。每个此类逻辑事务作用域都可以单独决定仅回滚状态,而外部事务作用域在逻辑上与内部事务作用域无关。当然,在标准PROPAGATION_REQUIRED行为的情况下,它们将被映射到同一物理事务。因此,内部事务范围中设置的仅回滚标记确实会影响外部事务实际提交的机会。但是,由于外部事务作用域本身并不决定回滚,因此回滚(由内部事务作用域无提示触发)在该级别上是意外的-这就是抛出UnexpectedRollbackException的原因。
相反,PROPAGATION_REQUIRES_NEW对每个受影响的事务范围使用完全独立的事务。在那种情况下,基础物理事务将有所不同,因此可以独立地进行提交或回滚,而外部事务不受内部事务回滚状态的影响。
PROPAGATION_NESTED再次有所不同,因为它使用具有多个可还原到的保存点的单个物理事务。这种部分回滚允许内部事务作用域触发其范围的回滚,尽管某些操作已被回滚,但外部事务仍能够继续物理事务。通常将其映射到JDBC保存点,因此仅适用于JDBC资源事务(Spring的DataSourceTransactionManager)。
为了完成讨论:如果应用程序本身未设置仅回滚标记,也可能引发UnexpectedRollbackException。相反,由于当前交易状态的限制,交易基础结构可能已决定唯一可能的结果是回滚。这与XA事务特别相关。
正如我上面所建议的,在内部事务范围内引发异常,然后在外部范围内捕获该异常,并将其转换为静默setRollbackOnly调用,这对于你的情况应该适用。这样,外部事务的调用者将永远不会看到异常。由于你仅担心由于调用方施加的特殊要求而导致的此类无提示回滚,因此我什至会争辩说,正确的体系结构解决方案是在服务层中使用异常,并将这些异常转换为服务外观级别的无提示回滚(正确然后再返回该特殊呼叫者)。
由于你的问题可能不仅与回滚异常有关,而且还与服务层引发的任何异常有关,因此你甚至可以在整个服务层中一直使用标准的异常驱动回滚,然后在事务处理完成后捕获并记录此类异常。已经完成,在一些适应性很强的服务外观中,可以将服务层的异常转换为特定于UI的错误状态。