从源码解析 Spring JDBC 异常抽象
初入学习 JDBC 操作数据库,想必大家都写过下面的代码:
数据库为:H2

如果需要处理特定 SQL 异常,比如 SQL 语句错误,这个时候我们应该怎么办?
查看 SQLException 源码,我们可以发现两个重要的方法。
SQLException.getErrorCode:返回数据库特定的错误码,由数据库厂商制定,不同厂商错误码不同。如重复主键错误码在 MySQL 中是 1062,而在 Oracle 中却是 1。
SQLException.getSQLState:返回 XOPEN 或 SQL: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:
- 默认转换类
- 主要根据 SQLException.getErrorCode 进行转换。
- 默认使用 SQLExceptionSubclassTranslator 作为 fallback 对象。
SQLExceptionSubclassTranslator:
- 基于 JDBC 的 SQLException 标准子类判断,如 java.sql.SQLTransientException。
- 使用 SQLStateSQLExceptionTranslator 作为 fallback 对象。
SQLStateSQLExceptionTranslator:
- 基于 SQLException.getSQLState 规则判断。
下面分析 SQLErrorCodeSQLExceptionTranslator ,其他两个比较类似,同学们可以自己看源码分析。
SQLErrorCodeSQLExceptionTranslator 转换器主要根据 SQLException.getErrorCode 进行判断。Spring 默认在 org/springframework/jdbc/support/sql-error-codes.xml 归纳不同数据库厂商相关错误码。该配置文件会在第一次发生 SQL 异常时由 SQLErrorCodesFactory 进行加载,最后生成 SQLErrorCodes。

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

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

自定义异常转换
上面说到 Spring 总共给我们留下三处扩展点。
- 继承 SQLErrorCodeSQLExceptionTranslator,重写 customTranslate。
- 继承 SQLExceptionTranslator,重写 translate,然后在 sql-error-codes.xml注入。
- 使用 SQLErrorCodes#customTranslations ,然后在 sql-error-codes.xml 配置相关错误码转换的规则。
第三种方式改动最小,比较简单。首先在 classpath 下生成 sql-error-codes.xml,复制原有配置,最后配置 customTranslations 。

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

总结
Spirng 异常处理将 SQL 异常转化成内置异常,屏蔽不同数据库返回码不一致的带来的问题。
最后总结本文的知识点,希望帮助到大家。

帮助
从源码解析 Spring JDBC 异常抽象的更多相关文章
- Spring5源码解析-Spring框架中的单例和原型bean
Spring5源码解析-Spring框架中的单例和原型bean 最近一直有问我单例和原型bean的一些原理性问题,这里就开一篇来说说的 通过Spring中的依赖注入极大方便了我们的开发.在xml通过& ...
- Mybatis源码解析1—— JDBC
在之前的文章中,我为大家介绍了 Mybatis 的详细用法,算是基础教程. 详细链接:Mybatis 基础教程 言归正传,只懂基础可不行,接下来将给大家带来高阶的源码解析教程,从浅入深,通过源码解析, ...
- SpringBoot源码学习1——SpringBoot自动装配源码解析+Spring如何处理配置类的
系列文章目录和关于我 一丶什么是SpringBoot自动装配 SpringBoot通过SPI的机制,在我们程序员引入一些starter之后,扫描外部引用 jar 包中的META-INF/spring. ...
- 源码解析Spring AOP的加载与生效
本次博主主要进行Spring AOP这里的解析,因为在工作中使用后,却不知道背后的实现原理并在使用的过程中发现了一些认知缺陷,所以决定写这么一篇文章以供大家参考参考,进入正题. 本次博主使用了@Asp ...
- spring源码解析——spring源码导入eclipse
一.前言 众所周知,spring的强大之处.几乎所有的企业级开发中,都使用了spring了.在日常的开发中,我们是否只知道spring的配置,以及简单的使用场景.对其实现的代码没有进行深入的了 ...
- Spring源码解析--Spring的整体架构
概述 Spring是一个开放源代码的设计层面框架,他解决的是业务逻辑层和其他各层的松耦合问题,因此它将面向接口的编程思想贯穿整个系统应用.Spring是于2003 年兴起的一个轻量级的Java 开发框 ...
- 深入源码解析spring aop实现的三个过程
Spring AOP的面向切面编程,是面向对象编程的一种补充,用于处理系统中分布的各个模块的横切关注点,比如说事务管理.日志.缓存等.它是使用动态代理实现的,在内存中临时为方法生成一个AOP对象,这个 ...
- 【Mybatis源码解析】- JDBC连接数据库的原理和操作
JDBC连接数据库的原理和操作 JDBC即Java DataBase Connectivity,java数据库连接:JDBC 提供的API可以让JAVA通过API方式访问关系型数据库,执行SQL语句, ...
- Spring源码解析系列汇总
相信我,你会收藏这篇文章的 本篇文章是这段时间撸出来的Spring源码解析系列文章的汇总,总共包含以下专题.喜欢的同学可以收藏起来以备不时之需 SpringIOC源码解析(上) 本篇文章搭建了IOC源 ...
随机推荐
- org.apache.catalina.webresources.Cache.backgroundProcess The background cache eviction process was unable to free [10] percent of the cache for Context [/filestore] - consider increasing the maximum s
需要耐心啊,太急于求成,希望直接就得到解决方法了...以至于正确方法都已经出现了,我却没有耐心看下去,所以反而又耽误了不少时间.... 项目加载100+张图片,还有一个小的MP4,所以console警 ...
- 深入理解FIFO(包含有FIFO深度的解释)——转载
深入理解FIFO(包含有FIFO深度的解释) FIFO: 一.先入先出队列(First Input First Output,FIFO)这是一种传统的按序执行方法,先进入的指令先完成并引退,跟着才执行 ...
- nslookup、dig命令Linux安装包
linux下提供nslookup,dig命令的软件就是 bind-utils yum install bind-utils -y
- XenServer 6.5 安装
为了方便截图我下面的所有操作都是在VMware Workstation 11 上面完成的,但在之后的所有Citrix产品的操作中都将会在物理环境完成,物理机安装XS的步骤和下面是相同的. 1.打开Wo ...
- golang连接orcale
使用glang有一段时间了,最开始其实并不太喜欢他的语法,但是后来熟悉之后发现用起来还挺爽的.之前数据库一直使用mysql,连接起来没有什么问题,github上有很多完善的驱动,所以以为连接其他数据库 ...
- 实现socket并发的几种方法
# 使用多进程实现socket聊天并发-server #服务端 import socket from multiprocessing import Process def server(conn,ad ...
- docker容器utf-8编码问题
locale -a export LANG=C.UTF-8 locale 在docker容器中python脚本汉字的会乱码 按照上面的方法暂时的设置下容器 编码可以解决脚本中文乱码问题
- [POJ 1000] A+B Problem 经典水题 C++解题报告 JAVA解题报告
A+B Problem Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 311263 Accepted: 1713 ...
- day01_03.人人都会编程
PHP if语句打招呼编程 <?php$gender = "man"; if($gender == "man"){ echo "you are ...
- Python守护进程、进程互斥锁、进程间通信ICP(Queue队列)、生产者消费者模型
知识点一:守护进程 守护进程:p1.daemon=True 守护进程其实就是一个“子进程“,守护=>伴随 守护进程会伴随主进程的代码运行完毕后而死掉 进程:当父进程需要将一个任务并发出去执行,需 ...