从源码解析-Spring-JDBC-异常抽象

36次阅读

共计 2102 个字符,预计需要花费 6 分钟才能阅读完成。

初入学习 JDBC 操作数据库,想必大家都写过下面的代码:

数据库为:H2

如果需要处理特定 SQL 异常,比如 SQL 语句错误,这个时候我们应该怎么办?

查看 SQLException 源码,我们可以发现两个重要的方法。

SQLException.getErrorCode: 返回数据库特定的错误码,由数据库厂商制定,不同厂商错误码不同。如重复主键错误码在 MySQL 中是 1062,而在 Oracle 中却是 1。

SQLException.getSQLState:返回 XOPENSQL: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 SQLExceptions](
https://docs.oracle.com/javas…

正文完
 0