1. 程式人生 > >從原始碼解析 Spring 資料庫異常抽理

從原始碼解析 Spring 資料庫異常抽理

初入學習 JDBC 操作資料庫,想必大家都寫過下面的程式碼:

資料庫為:H2

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

檢視 SQLException 原始碼,我們可以發現兩個重要的方法。

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

SQLException.getSQLState:返回 XOPEN 或 SQL:2003 制定的錯誤碼規範。資料庫廠商會將不同錯誤訊息對映成同一個錯誤碼

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

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

下面我們使用 Spring 操作資料庫。

Spring 操作資料庫

使用 Spring 之後,我們不再需要強制捕獲異常。如果 SQL 語句執行存在異常,Spring 會丟擲其內建特定的異常。如上面 SQL 語句異常將會丟擲 BadSqlGrammarException。除了這個異常之外,Spring 還定義很多資料庫異常。

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

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

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

實現細節

除錯 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 提供擴充套件方法,可以根據錯誤碼轉換成自定義的異常。

最後檢視 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 。

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

總結

Spirng 異常處理將 SQL 異常轉化成內建異常,遮蔽不同資料庫返回碼不一致的帶來的問題。

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

幫助

Handling SQLExcepti