• 从源码解析 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

  • 相关阅读:
    monorepo使用教程
    pnpm教程
    Vite 从入门到精通,玩转新时代前端构建法则
    browserslist 目标浏览器配置表
    VS项目属性的一些配置项的总结
    FastAPI入门教程(持续更新中)
    FastAPI 学习之路(六十)打造系统的日志输出
    FastAPI 学习之路(六十一)使用mysql数据库替换sqlite数据库
    FastAPI 学习之路(五十九)封装统一的json返回处理工具
    FastAPI 学习之路(五十八)对之前的代码进行优化
  • 原文地址:https://www.cnblogs.com/goodAndyxublog/p/10922497.html
Copyright © 2020-2023  润新知