从源码解析 Spring 数据库异常抽理
初入学习 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 异常转化成内置异常,屏蔽不同数据库返回码不一致的带来的问题。
最后总结本文的知识点,希望帮助到大家。
帮助
上一篇: 中心极限定理