前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >从源码解析 Spring JDBC 异常抽象

从源码解析 Spring JDBC 异常抽象

作者头像
andyxh
发布2019-09-10 19:34:29
1.1K0
发布2019-09-10 19:34:29
举报
文章被收录于专栏:程序通事程序通事

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

数据库为:H2

1558763379012ad6cfc3d5bf34e2d8b249fbc21ded14a.png
1558763379012ad6cfc3d5bf34e2d8b249fbc21ded14a.png

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

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

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

SQLException.getSQLState:返回 XOPENSQL:2003 制定的错误码规范。数据库厂商会将不同错误消息映射成同一个错误码

所以我们可以根据 SQLException.getErrorCode 处理相应的数据库异常。

15587636977529093ef116cbd4a4e9038656c7e697778.png
15587636977529093ef116cbd4a4e9038656c7e697778.png

由于数据库厂商错误码不相同,这就导致如果我们更换数据库,上面判断逻辑就必须重写。

下面我们使用 Spring 操作数据库。

Spring 操作数据库

155876376989026d5d088813443e184fef4d247fe79e9.png
155876376989026d5d088813443e184fef4d247fe79e9.png

使用 Spring 之后,我们不再需要强制捕获异常。如果 SQL 语句运行存在异常,Spring 会抛出其内置特定的异常。如上面 SQL 语句异常将会抛出 BadSqlGrammarException。除了这个异常之外,Spring 还定义很多数据库异常。

Spring 数据库异常
Spring 数据库异常

每个 Spring 数据库异常的基类都是 DataAccessException。由于 DataAccessException 继承自 RuntimeException,所以在这类异常无需强制捕获。

在 Spring 中使用 SQLExceptionTranslator 进行异常转换,默认的转换规则会根据 SQLException.getErrorCode 返回的错误码进行相应的转换。

下面我们从源码分析转换过程。

实现细节

调试 JdbcTemplate 的源码。

155876425383535e0fb8314914f4aa9fd86d97d2c212b.png
155876425383535e0fb8314914f4aa9fd86d97d2c212b.png

可以看到这里捕获了 SQLException,转换之后再将其抛出。

整个转换过程,最后交给 SQLExceptionTranslator 进行转换。

首先我们查看 SQLExceptionTranslator 类图。

1558764498907d72f41852e874c6ca6b370216f8f0243.png
1558764498907d72f41852e874c6ca6b370216f8f0243.png

可以看到其实现了一个抽象类以及三个子类。

1558764729835900605b4b26f45fa97c9a565ea6402c5.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
15587650308278d7e4c1ddce64b8399d284f7de62653c.png

另外在 SQLErrorCodes 提供扩展方法,可以根据错误码转换成自定义的异常。

最后查看 SQLErrorCodeSQLExceptionTranslator 里的转换方法。

155876551332300ff7f1c4bc04d70ace8d5ee9e9dfadb.png
155876551332300ff7f1c4bc04d70ace8d5ee9e9dfadb.png

前三个方法是 Spring 留下扩展方法,可以根据自己需求分别扩展。若都没有实现,将会根据错误码判断转换成具体的异常。

1558765591114e6e8e9f273da4cd2a3fcbf95eabbd449.png
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
1558765757759d187567a9d4e46d98d0ad7a92ce47fc8.png

这里需要注意的是,需要转化的异常类型必须为 DataAccessException 子类。下面面我们自定义一个异常。

155876581690994d8583832b849b5be65e40e32299558.png
155876581690994d8583832b849b5be65e40e32299558.png

总结

Spirng 异常处理将 SQL 异常转化成内置异常,屏蔽不同数据库返回码不一致的带来的问题。

最后总结本文的知识点,希望帮助到大家。

155876696761376682d3e84f647d7a70aadd6811f111f.png
155876696761376682d3e84f647d7a70aadd6811f111f.png

帮助

Handling SQLExceptions

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019-05-25 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Spring 操作数据库
  • 实现细节
  • 自定义异常转换
  • 总结
  • 帮助
相关产品与服务
数据库
云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档