如何使 Spring @Transactional 回滚所有未捕获的异常?

How to make Spring @Transactional roll back on all uncaught exceptions?

我的 Spring/Java 网络应用程序有 @Transactional 可以访问数据库的服务:

@Transactional
public class AbstractDBService  { ... }

所需的功能适用于向上传播到服务层之外以导致回滚的任何 未捕获 throwable。有点惊讶这不是默认行为,但经过一些谷歌搜索尝试后:

@Transactional(rollbackFor = Exception.class)

除非故意吞下异常而不是重新抛出异常,否则这似乎有效。 (特殊情况是找不到实体时。我猜这可以重新设计为不抛出异常,但预计不可避免地会有其他异常 - 例如,使用时想到的是 InterruptedException Thread.sleep()). 然后 Spring 抱怨:

org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Transaction marked as rollbackOnly ...truncated.. Caused by: javax.persistence.RollbackException: Transaction marked as rollbackOnly at org.hibernate.jpa.internal.TransactionImpl.commit(TransactionImpl.java:58) at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:517)

我在这里遗漏了什么吗?...有没有办法告诉 Spring 回滚 所有未捕获的 throwables

如果你想回滚所有未捕获的 Throwable,你可以在注释中指定:

@Transactional(rollbackFor = Throwable.class)

默认情况下 Spring 不会回滚 Error 子类,可能是因为一旦抛出错误,JVM 是否会处于足够好的状态来对它做任何事情似乎值得怀疑,在这一点上交易可能会超时。 (如果您在引发 OutOfMemoryError 时尝试回滚,最有可能的结果是另一个 OutOfMemoryError。)所以您可能不会从中获得太多。

当你提到吞噬异常的情况时,Spring 不可能知道它,因为异常没有找到通往 Spring 的代理(这是实现交易功能)。这就是您的 RollbackException 示例中发生的情况,Hibernate 已经确定事务需要回滚,但是 Spring 没有收到备忘录,因为有人吃了异常。所以 Spring 没有回滚事务,它认为一切正常并尝试提交,但由于 Hibernate 已将事务标记为仅回滚,因此提交失败。

答案是不要吞下那些异常,而是让它们被抛出;让它们不受检查应该会让你更容易做正确的事。应该设置一个异常处理程序来接收从控制器抛出的异常,在应用程序的任何级别抛出的大多数异常都可以在那里捕获并记录。