說說 Spring DAO 的異常體系

deniro發表於2019-03-02

Spring 提供了一套和實現技術無關的 、 面向 DAO 層語義級別的異常體系,內部通過轉換器將不同持久化技術的異常轉換成 Spring 的異常,實現統一管理。

1 異常體系

很多正統的 AP中,使用了過多的檢查型異常,以致於在使用 API 時,程式碼中充斥了大量 try/catch 樣板式的程式碼 。 大多數情況下,這些 catch 程式碼段除了記錄日誌外,並沒有做多少其它有益的工作。

比如 JDK 中的 JDBC API,大家都說不好用,因為檢查型異常氾濫,許多異常處理程式碼喧賓奪主地侵入到業務程式碼中,從而破壞了整體程式碼的整潔與優雅 。

Spring 在 org.springframework.dao 中提供了一套優雅的 DAO 異常體系, 這些異常都繼承自 DataAccessExceptionDataAccessException 繼承自NestedRuntimeException, NestedRuntimeException 異常以巢狀的方式封裝了源異常 。 因此,雖然不同的持久化技術的特定異常被轉換到 Spring 的 DAO 異常體系中,但我們可以通過 getCause() 方法獲取原始異常資訊 。

說說 Spring DAO 的異常體系

這套異常體系從 DAO 的抽象層次上定義了異常目錄樹,它使得開發者可以很容易地關注某個特定的語義異常。而 JDBC 的 SQLException 過於底層,而且與具體資料庫強相關(比如 getErrorCode()),不僅不好編碼,而且很難移植。

Spring 建立了異常分類目錄,以適當的顆粒度劃分了異常型別。這樣做的好處是:

  • 開發者可以從底層繁瑣複雜的技術細節中解脫出來。
  • 開發者可以選擇自己感興趣的異常進行處理 。

DataAccessException 下有這些異常子類:

異常子類 說明
CleanupFailureDataAccessException 執行 DAO 操作成功,但在釋放資料資源時發生異常,如關閉 Connection 時發生異常。
ConcurrencyFailureException 併發地運算元據時發生異常,如無法獲取樂觀鎖或悲觀鎖時、死鎖引發的失敗等場景。
DataAccessResourceFailureException 訪問資料資源失敗,如無法獲取資料連線,無法獲取 Hibernate 的會話等場景。
DataRetrievalFailureException 獲取資料失敗,如找不到對應主鍵的資料或使用了錯誤的列索引等場景。
DataSourceLookupFailureException 無法從 JNDI 中查詢到資料來源。
DataIntegrityViolationException 資料操作違反了資料一致性限制時丟擲,如插入重複的主鍵或引用不存在的外來鍵場景。
InvalidDataAccessApiUsageException 不正確地呼叫某一種持久化技術時丟擲,如在 Spring JDBC 中查詢物件在呼叫前沒有事先進行編譯操作,就會丟擲該異常。這種異常主要是因為不正確地使用持久化技術而產生的。
InvalidDataAccessResourceUsageException 在訪問資料來源時使用了不正確的方法時丟擲,如寫錯 SQL 語句。
PermissionDeniedDataAccessException 資料訪問許可權不足時丟擲。如僅擁有隻讀許可權卻試圖更改資料。
UncategorizedDataAccessException 其它未被分類的異常。

Spring 為了進一步細化錯誤問題域, 它對上述的這些一級異常類又進行了細分。

這套異常體系具有高度的可擴充套件性,當 Spring 需要對一個新的持久化技術提供支援時,只要為其定義一個對應的子異常即可,這種方式實現了設計模式中的“開閉原則” 。

開閉原則( OCP )是物件導向設計中 “ 可複用設計 ” 的基石,是物件導向設計中最重要的原則之一,其它很多的設計原則都是實現開閉原則的一種手段 。 對於擴充套件是開放的,對於修改是關閉的,這意味著模組的行為是可以擴充套件的 。 當應用的需求改變時,我們可以對模組進行擴充套件,使其具有滿足那些改變的新行為 。

2 異常轉換器

2.1 JDBC

一般情況下,JDBC API 在執行資料操作出現異常時,大都會丟擲 SQLException ,SQLException 把異常的細節封裝在異常屬性中,所以如果希望瞭解異常的具體原因,我們必須對異常屬性進行分析。

SQLException 擁有兩個代表異常具體原因的屬性:

屬性 型別 說明
錯誤碼 int 與具體資料庫相關,呼叫 getErrorCode() 返回。
SQL 狀態碼 String 標準錯誤程式碼,由 5 個字元組成,呼叫 getSQLState() 返回。

Spring 會根據錯誤碼和 SQL 狀態碼將 SQLExeption 轉換為對應的 Spring DAO 異常 。 在 org.springframework.jdbc.support 包中定義了 SQLExceptionTranslator 介面,該介面的兩個實現類 SQLErrorCodeSQLExceptionTranslator 和 `SQLStateSQLExceptionTranslator
分別負責處理 SQLException 中錯誤程式碼和 SQL 狀態碼的轉換工作 。

說說 Spring DAO 的異常體系

2.2 其它 ORM 持久化技術

其它 ORM 持久化技術都擁有一個語義明確的異常體系,所以轉換相對簡單。

**注意:**Spring4 只支援 Hibernate3.6+。

Spring 在 org.springframe.orm 中為所支援的 ORM 技術定義了相應的子包。對應的異常轉換器也定義在這些子包中:

ORM 持久化技術 異常轉換器
HibernateX,X 可為 3、4 或 5 org.springframework.orm.hibernateX.SessionFactoryUtils
JPA org.springframework.orm.jpa.EntityManagerFactoryUtils
JDO org.springframework.orm.jdo.PersistenceManagerFactoryUtils

這些工具類除了具有異常轉換的功能外,在進行事務管理時,還提供了從事務上下文環境中返回相同會話的功能 。

Spring 也支援 myBatis 持久化技術,因為 myBatis 丟擲的異常與 JDBC 相同, 都是 SQLException 異常,所以採用了和 JDBC 相同的異常轉換器 。

相關文章