spring 数据访问异常体系将 jdbc 的 sqlexception 统一转换为非检查型、语义清晰的 dataaccessexception 子类,由 sqlexceptiontranslator 自动完成,集成于 jdbctemplate、spring data jpa、mybatis-spring 及 @transactional 中,避免裸 jdbc 导致的异常处理混乱与事务失效。

Spring 的数据访问异常体系正是为解决这个问题而生——它把 JDBC 原生的 SQLException(检查型、驱动相关、语义模糊)统一转换为一组非检查型、语义清晰、与具体技术解耦的运行时异常,比如 DataAccessException 及其子类(DuplicateKeyException、DataIntegrityViolationException、CannotAcquireLockException 等)。这个转化不是简单包装,而是由 Spring 的 异常翻译器(SQLExceptionTranslator) 在底层自动完成,无需手动 catch-throw-rethrow。
依赖 Spring 的模板类或声明式事务自动触发转化
只要使用 Spring 封装的数据访问组件,异常转化就默认启用:
-
JdbcTemplate:所有执行方法(
query()、update()、execute())内部已集成SQLExceptionTranslator,抛出的必是DataAccessException子类 -
Spring Data JPA / MyBatis-Spring 集成:Repository 接口方法或
@Mapper方法抛出的数据库异常,经 Spring AOP 拦截后同样被翻译 -
声明式事务(@Transactional):无论底层用 JDBC 还是 Hibernate,事务回滚判定都基于
DataAccessException层级,不关心原始驱动报错类型
避免裸写 DriverManager.getConnection + try-catch SQLException
直接调用 JDBC API 会绕过 Spring 异常体系,导致两种问题:
- 业务代码被迫处理
SQLException,需手动解析getSQLState()或getErrorCode()判定错误类型(如 MySQL 的 1062 表示主键冲突,PostgreSQL 的 23505) - 事务管理失效:Spring 无法识别你 catch 后吞掉或重新 throw 的原始异常,可能造成该回滚却未回滚
- 正确做法是交由
JdbcTemplate或DataSourceUtils.getConnection()获取连接,让 Spring 全程托管资源与异常
自定义 SQLExceptionTranslator 应对特殊驱动行为
多数场景下 Spring 内置的 SQLStateSQLExceptionTranslator(基于 SQLState 标准)或 VendorAwareSQLExceptionTranslator(适配厂商错误码)已足够。但若遇到以下情况,可扩展:
- 某国产数据库返回非标错误码,需映射为
DeadlockLoserDataAccessException - 希望将特定超时异常(如网络中断)统一转为
CannotGetJdbcConnectionException而非默认的UncategorizedSQLException - 实现方式:继承
SQLExceptionTranslator,重写translate()方法,在DataSourceTransactionManager或JdbcTemplate中通过setExceptionTranslator()注入
ORM 层保持异常语义一致性,不暴露底层 JDBC 细节
当从 JDBC 迁移到 MyBatis 或 JPA 时,上层服务逻辑不应感知变化:
- 插入重复主键 → 均抛
DuplicateKeyException,而非 JDBC 的MySQLIntegrityConstraintViolationException或 JPA 的EntityExistsException - 外键约束失败 → 统一为
DataIntegrityViolationException - 这意味着你的
try-catch块只需捕获 Spring 的异常体系,DAO 层换技术栈时,业务异常处理代码零修改










