從源碼解析 Spring JDBC 異常抽象

初入學習 JDBC 操作數據庫,想必大家都寫過下面的代碼:

數據庫爲:H2

JDBC 操作數據庫

如果需要處理特定 SQL 異常,比如 SQL 語句錯誤,這個時候我們應該怎麼辦?

查看 SQLException 源碼,我們可以發現兩個重要的方法。

SQLException.getErrorCode:返回數據庫特定的錯誤碼,由數據庫廠商制定,不同廠商錯誤碼不同。如重複主鍵錯誤碼在 MySQL 中是 1062,而在 Oracle 中卻是 1。

SQLException.getSQLState:返回 XOPENSQL:2003 制定的錯誤碼規範。數據庫廠商會將不同錯誤消息映射成同一個錯誤碼

所以我們可以根據 SQLException.getErrorCode 處理相應的數據庫異常。

JDBC 異常處理

由於數據庫廠商錯誤碼不相同,這就導致如果我們更換數據庫,上面判斷邏輯就必須重寫。

下面我們使用 Spring 操作數據庫。

Spring 操作數據庫

Spring 數據庫處理

使用 Spring 之後,我們不再需要強制捕獲異常。如果 SQL 語句運行存在異常,Spring 會拋出其內置特定的異常。如上面 SQL 語句異常將會拋出 BadSqlGrammarException。除了這個異常之外,Spring 還定義很多數據庫異常。

Spring 數據庫異常

每個 Spring 數據庫異常的基類都是 DataAccessException。由於 DataAccessException 繼承自 RuntimeException,所以在這類異常無需強制捕獲。

在 Spring 中使用 SQLExceptionTranslator 進行異常轉換,默認的轉換規則會根據 SQLException.getErrorCode 返回的錯誤碼進行相應的轉換。

下面我們從源碼分析轉換過程。

實現細節

調試 JdbcTemplate 的源碼。

JdbcTemplate

可以看到這裏捕獲了 SQLException,轉換之後再將其拋出。

整個轉換過程,最後交給 SQLExceptionTranslator 進行轉換。

首先我們查看 SQLExceptionTranslator 類圖。

類圖

可以看到其實現了一個抽象類以及三個子類。

抽象方法

抽象類中會首先會使用子類轉換,若未能轉換成功,將會啓動 fallback機制,再次轉換,作爲兜底。

接着我們先看下三個子類的區別。

SQLErrorCodeSQLExceptionTranslator:

  1. 默認轉換類
  2. 主要根據 SQLException.getErrorCode 進行轉換。
  3. 默認使用 SQLExceptionSubclassTranslator 作爲 fallback 對象。

SQLExceptionSubclassTranslator

  1. 基於 JDBC 的 SQLException 標準子類判斷,如 java.sql.SQLTransientException
  2. 使用 SQLStateSQLExceptionTranslator 作爲 fallback 對象。

SQLStateSQLExceptionTranslator

  1. 基於 SQLException.getSQLState 規則判斷。

下面分析 SQLErrorCodeSQLExceptionTranslator ,其他兩個比較類似,同學們可以自己看源碼分析。

SQLErrorCodeSQLExceptionTranslator 轉換器主要根據 SQLException.getErrorCode 進行判斷。Spring 默認在 org/springframework/jdbc/support/sql-error-codes.xml 歸納不同數據庫廠商相關錯誤碼。該配置文件會在第一次發生 SQL 異常時由 SQLErrorCodesFactory 進行加載,最後生成 SQLErrorCodes

SQLErrorCodes

另外在 SQLErrorCodes 提供擴展方法,可以根據錯誤碼轉換成自定義的異常。

最後查看 SQLErrorCodeSQLExceptionTranslator 裏的轉換方法。

SQLErrorCodeSQLExceptionTranslator

前三個方法是 Spring 留下擴展方法,可以根據自己需求分別擴展。若都沒有實現,將會根據錯誤碼判斷轉換成具體的異常。

默認轉換規則

自定義異常轉換

上面說到 Spring 總共給我們留下三處擴展點。

  1. 繼承 SQLErrorCodeSQLExceptionTranslator,重寫 customTranslate
  2. 繼承 SQLExceptionTranslator,重寫 translate,然後在 sql-error-codes.xml注入。
  3. 使用 SQLErrorCodes#customTranslations ,然後在 sql-error-codes.xml 配置相關錯誤碼轉換的規則。

第三種方式改動最小,比較簡單。首先在 classpath 下生成 sql-error-codes.xml,複製原有配置,最後配置 customTranslations

customTranslations

這裏需要注意的是,需要轉化的異常類型必須爲 DataAccessException 子類。下面面我們自定義一個異常。

自定義異常

總結

Spirng 異常處理將 SQL 異常轉化成內置異常,屏蔽不同數據庫返回碼不一致的帶來的問題。

最後總結本文的知識點,希望幫助到大家。

知識圖譜

幫助

[Handling SQLExceptions](
https://docs.oracle.com/javas...

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章