2015-11-04 59 views
3

我的春天/ Java Web应用程序具有@Transactional服务,可以接触到数据库:如何使Spring @Transactional回滚所有未捕获的异常?

@Transactional 
public class AbstractDBService { ... } 

所需的功能为任何未捕获抛出该向上传播超越了服务层引起回滚。有点惊讶,这不是默认行为,但有点谷歌搜索后尝试:

@Transactional(rollbackFor = Exception.class) 

这似乎除了当一个例外是故意吞食,而不是重新引发工作。 (具体情况是没有找到实体的时候,猜猜这可能是重新设计的,不会抛出异常,但期望不可避免会出现其他情况 - 例如使用Thread.sleep()时想到的是InterruptedException)。 Then Spring complains:

org.springframework.transaction.TransactionSystemException:无法提交JPA事务;嵌套的例外是 javax.persistence.RollbackException:事务标记为 rollbackOnly ... ..截断 产生的原因:javax.persistence.RollbackException:事务标记为rollbackOnly 在org.hibernate.jpa.internal。 TransactionImpl.commit(TransactionImpl.java:58) 在org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:517)

我失去了一些东西在这里?......有没有一种办法告诉Spring回滚所有未捕获的throwables

+0

我很困惑。您当前的'rollbackFor'应该使'@ Transactional'代理回滚从注释方法中抛出的任何'Exception'。那不是你看到的吗? –

+0

我看到,但它*也*似乎使这种'Exception'被捕获,而不是重新抛出时,回滚尝试回滚。 ([这个答案](http://stackoverflow.com/questions/19302196/transaction-marked-as-rollback-only-how-do-i-find-the-cause#19311268)似乎支持这一点。) –

回答

7

如果要回滚上的所有未捕获将Throwable,您可以指定在注解:

@Transactional(rollbackFor = Throwable.class) 

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

当你提到吞咽异常的情况时,Spring就没有办法知道它是因为异常没有找到Spring的代理(它正在实现事务功能)的方式。这就是你的RollbackException例子中发生的事情,Hibernate已经计算出需要回滚的事务,但是Spring没有得到备忘录,因为有人吃掉了异常。所以Spring并没有将事务回滚,它认为一切正常并尝试提交,但是由于Hibernate标记了事务回滚,提交失败。

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

+0

非常感谢您的详细解答。关于Spring如何在发动机罩下工作的一些很好的见解 - 对我来说,这全是“魔法”......但是一旦你开始寻找猜测,一些类型的魔法是可能的,而有些则不是!尽管如此,仍然认为这有点令人遗憾。在我们不关心并想要吞咽并继续的例外情况下,使用了第三方实用程序库。好的,可以开始为特定异常添加'noRollbackFor',但是如果它是一种广泛常见的异常类型呢?......无论如何,这是有点假设的,听起来像是体系结构的限制。 –

+0

@Steve:你仍然可以做任何你需要的异常处理,包括吃掉异常。只要知道你想要的效果是在当前的交易。顺便说一句,默认情况下,你可以避免回滚检查异常。 (实际上,在很多情况下,默认工作都非常好,正如我在第一部分关于回滚Error的部分中所述)。 –