2015-11-04 94 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的部分中所述)。 –