JDBC API 4.2(十二):SQLException 源碼分析

1、簡介

當JDBC在與數據源交互期間遇到錯誤時,它將拋出SQLException實例,而不是Exception。

在這裏插入圖片描述

SQLException 實例包含以下信息,可以幫助您確定錯誤原因:

1、error 的描述: 調用方法SQLException.getMessage檢索包含此描述的String對象。

2、SQLState 代碼: 這些代碼及其各自的含義已由 ISO/ANSI 和 Open Group (X/Open) 進行了標準化,另外還有些代碼留給數據庫供應商自己定義。 此String對象由五個字母數字字符組成。 通過調用方法 SQLException.getSQLState 檢索此代碼。

3、錯誤代碼: 這是一個整數值,用於標識導致引發SQLException實例的錯誤。 它的值和含義是特定於實現的,並且可能是基礎數據源返回的實際錯誤代碼。 通過調用方法SQLException.getErrorCode檢索錯誤。

4、原因: SQLException 實例可能具有因果關係,該因果關係由導致拋出SQLException實例的一個或多個Throwable對象組成。要獲取這一系列原因信息,要遞歸調用方法SQLException.getCause 獲取,直到返回空值。

5、對任何鏈接的異常的引用: 如果發生多個錯誤,則可以通過該異常引用鏈獲取異常。 通過對引發的異常調用方法 SQLException.getNextException 來檢索這些異常。

2、異常(Exceptions)

下面我們編寫一個通用方法來檢索SQLException中包含的SQLState,錯誤代碼,錯誤描述和原因(如果有的話)以及與其鏈接的任何其他異常:

  private static String sql = "select id,name,email,country,password from Userswhere id=1";

    public static void main(String[] args) {
        // Step 1: 創建連接對象 connection
        try (Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/lkf_db?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT", "root", "root");
             // Step 2:使用 connection 創建 stmt
             Statement stmt = connection.createStatement();
             // Step 3:執行SQL語句
             ResultSet rs = stmt.executeQuery(sql)) {
            //獲取結果集 rs 的元數據對象 resultSetMetaData
            ResultSetMetaData resultSetMetaData = rs.getMetaData();
            System.out.println("1. 列數量 :: " + resultSetMetaData.getColumnCount());
            System.out.println("2. 第一列名稱 :: " + resultSetMetaData.getColumnName(1));
            System.out.println("3. 數據庫名稱 :: " + resultSetMetaData.getCatalogName(1));
            ;
            System.out.println("4. 列數據類型 :: " + resultSetMetaData.getColumnTypeName(1));
            System.out.println("5. 表名 :: " + resultSetMetaData.getTableName(1));
        } catch (SQLException e) {
            printSQLException(e);
        }
    }

    public static void printSQLException(SQLException ex) {
        for (Throwable e : ex) {
            if (e instanceof SQLException) {
                if (!ignoreSQLException(((SQLException) e).getSQLState())) {
                    e.printStackTrace(System.err);
                    System.err.println("SQLState: " + ((SQLException) e).getSQLState());
                    System.err.println("Error Code: " + ((SQLException) e).getErrorCode());
                    System.err.println("Message: " + e.getMessage());
                    Throwable t = ex.getCause();
                    while (t != null) {
                        System.out.println("Cause: " + t);
                        t = t.getCause();
                    }
                }
            }
        }
    }

    public static boolean ignoreSQLException(String sqlState) {
        if (sqlState == null) {
            System.out.println("The SQL state is not defined!");
            return false;
        }

        // X0Y32: Jar文件已存在
        if (sqlState.equalsIgnoreCase("X0Y32"))
            return true;

        // 42Y55: 該表已存在
        if (sqlState.equalsIgnoreCase("42Y55"))
            return true;
        return false;
    }

輸出結果如下:

java.sql.SQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '=1' at line 1
	at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:120)
	at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:97)
	at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:122)
	at com.mysql.cj.jdbc.StatementImpl.executeQuery(StatementImpl.java:1218)
	at com.lkf.jdbc.SQLExceptionDemo.main(SQLExceptionDemo.java:13)
SQLState: 42000
Error Code: 1064
Message: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '=1' at line 1

3、警告(Warnings)

SQLWarning 對象是SQLException的子類,用於處理數據庫訪問警告。 警告不會像異常一樣終止應用程序的執行。 它們只是警告用戶某些事情沒有按計劃進行。

在這裏插入圖片描述

以下方法說明了如何獲取有關Statement或ResultSet對象上報告的所有警告的完整信息:

public static void getWarningsFromResultSet(ResultSet rs)
            throws SQLException {
        printWarnings(rs.getWarnings());
    }

    public static void getWarningsFromStatement(Statement stmt)
            throws SQLException {
        printWarnings(stmt.getWarnings());
    }

    public static void printWarnings(SQLWarning warning)
            throws SQLException {

        if (warning != null) {
            System.out.println("\n---Warning---\n");

            while (warning != null) {
                System.out.println("Message: " + warning.getMessage());
                System.out.println("SQLState: " + warning.getSQLState());
                System.out.print("Vendor error code: ");
                System.out.println(warning.getErrorCode());
                System.out.println("");
                warning = warning.getNextWarning();
            }
        }
    }

最常見的警告是DataTruncation警告,它是SQLWarning的子類。 所有DataTruncation對象的SQLState均爲01004,表示讀取或寫入數據有問題。

使用DataTruncation方法,您可以找出在哪個列或參數數據中被截斷,截斷是在讀取還是寫入操作上進行的,應該傳送多少字節以及實際傳送了多少字節。

4、SQLException 分類

JDBC驅動程序可能會拋出SQLException的子類,該子類對應於與特定SQLState類值不相關的常見SQLState或常見錯誤狀態。 這使我們可以編寫更多可移植的錯誤處理代碼。 這些異常是以下類別之一的子類:

  • SQLNonTransientException
  • SQLTransientException
  • SQLRecoverableException

在這裏插入圖片描述

5、SQLException的其他子類

BatchUpdateException

在批處理更新操作期間發生錯誤時,將引發BatchUpdateException。 除了SQLException提供的信息外,BatchUpdateException還提供錯誤發生之前已執行的所有語句的更新計數。

SQLClientInfoException

無法在連接上設置一個或多個客戶端信息屬性時,將引發SQLClientInfoException。 除了SQLException提供的信息外,SQLClientInfoException還提供未設置的客戶端信息屬性的列表。

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