2011-08-19 59 views
18

我有一個Spring管理的服務方法來管理數據庫插入。它包含多個插入語句。爲什麼事務回滾RuntimeException但不SQLException

@Transactional 
public void insertObservation(ObservationWithData ob) throws SQLException 
{ 
    observationDao.insertObservation(ob.getObservation()); 
      // aop pointcut inserted here in unit test 
    dataDao.insertData(ob.getData()); 
} 

我有兩個單元測試,在調用第二個插入之前拋出異常。如果該異常是RuntimeException,則該事務將回滾。如果異常是SQLException,則第一次插入將被保留。

我很困惑。誰能告訴我爲什麼事務不會在SQLException上回滾?任何人都可以提供一個建議如何管理這個?我可以捕獲SQLException並拋出一個RuntimeException,但這看起來很奇怪。

+2

根據skaffman的回答和Nathan的提示,我已經捕獲了DAO層的所有SQLException,並使用Spring的SQLExceptionTranslator將它們轉換爲未檢查的DataAccessExceptions。這篇春季論壇帖子,http://forum.springsource.org/showthread.php?63549-Conceptual-context-information,以及Robert Martin在「Clean Code。敏捷軟件工藝手冊」中的錯誤處理章節(Prentice Hall,2008年)也有助於澄清爲什麼拋出未經檢查的異常保持更好的模塊性。 – climmunk

回答

35

這是定義的行爲。從docs

任何RuntimeException觸發器回滾,任何檢查的異常不。

這是所有Spring事務API的常見行爲。默認情況下,如果從事務代碼中引發RuntimeException,則事務將被回滾。如果檢查異常(即不是RuntimeException)被拋出,則該事務不會被回滾。

這背後的基本原理是RuntimeException類通常被Spring用來表示不可恢復的錯誤條件。

如果您希望這樣做,可以將此行爲從默認值更改,但如何執行此操作取決於您如何使用Spring API以及如何設置事務管理器。

+0

是的,這是正確的,雖然我不完全明白這一點。我想拋出檢查的SQLException,以便應用程序代碼可以處理它(記錄,繼續,停止執行等),但我不希望事務成功。我可以設置每個事務註釋以回滾異常,但我寧願將事務管理器設置爲在整個應用程序範圍內執行。我不能立即明白我可以配置基本的DataSourceTransactionManager來做到這一點。如果你有更豐富的討論的建議,我會很感激。我現在正在進入春季論壇。謝謝! – climmunk

2

對於沒有從異常中恢復的情況,Spring會大量使用RuntimeExceptions(包括使用DataAccessExceptions來封裝ORM中的SQLExceptions或異常)。它假設你想使用檢查異常來處理需要通知某個層的服務層,但你不希望事務受到干擾。

如果您使用的是Spring,您不妨使用它的jdbc-wrapping庫和DataAccessException轉換工具,它將減少必須維護的代碼量並提供更有意義的異常。還有一個服務層拋出特定於實現的異常是一種難聞的氣味。 Spring之前的方式是創建與實現無關的檢查異常,包裝特定於實現的異常,這導致大量繁忙工作和臃腫的代碼庫。 Spring的方式避免了這些問題。

如果你想知道爲什麼Spring選擇以這種方式工作,可能是因爲他們使用AOP來添加事務處理。它們不能更改它們包裝的方法的簽名,因此檢查異常不是一種選擇。