從源碼解析 Spring JDBC 異常抽象


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

數據庫為:H2

1558763379012ad6cfc3d5bf34e2d8b249fbc21ded14a.png

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

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

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

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

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

15587636977529093ef116cbd4a4e9038656c7e697778.png

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

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

Spring 操作數據庫

155876376989026d5d088813443e184fef4d247fe79e9.png

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

Spring 數據庫異常

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

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

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

實現細節

調試 JdbcTemplate 的源碼。

155876425383535e0fb8314914f4aa9fd86d97d2c212b.png

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

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

首先我們查看 SQLExceptionTranslator 類圖。

1558764498907d72f41852e874c6ca6b370216f8f0243.png

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

1558764729835900605b4b26f45fa97c9a565ea6402c5.png

抽象類中會首先會使用子類轉換,若未能轉換成功,將會啟動 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

15587650308278d7e4c1ddce64b8399d284f7de62653c.png

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

最后查看 SQLErrorCodeSQLExceptionTranslator 里的轉換方法。

155876551332300ff7f1c4bc04d70ace8d5ee9e9dfadb.png

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

1558765591114e6e8e9f273da4cd2a3fcbf95eabbd449.png

自定義異常轉換

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

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

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

1558765757759d187567a9d4e46d98d0ad7a92ce47fc8.png

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

155876581690994d8583832b849b5be65e40e32299558.png

總結

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

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

155876696761376682d3e84f647d7a70aadd6811f111f.png

幫助

Handling SQLExceptions


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM