Java全局异常处理需逐层遍历getCause()定位最内层SQLException,再解析其SQLState、errorCode及消息提取约束类型与字段,结合@ControllerAdvice统一返回结构化错误响应。

Java中全局异常处理要从异常链的 Cause 中提取真实的数据库约束报错,关键在于**逐层向上遍历 getCause(),定位到最内层的 SQL 异常(如 SQLException),再解析其 SQLState、errorCode 和消息内容**。Spring 的 @ControllerAdvice + @ExceptionHandler 是常用入口,但仅捕获顶层异常(如 DataIntegrityViolationException)远远不够——它只是 Spring 封装的包装类,真正的约束信息藏在底层 SQLException 的 cause 链里。
定位最深层的 SQLException
Spring 的数据访问异常(如 DataIntegrityViolationException、DuplicateKeyException)都继承自 DataAccessException,它们内部通过 cause 持有原始 JDBC 异常。需递归获取 getCause(),直到找到 SQLException 或其子类(如 PSQLException、MySQLSyntaxErrorException):
- 不要只调用一次
getCause()—— 常见链长为:Spring 异常 →SQLException,或 Spring 异常 →TransactionSystemException→SQLException - 推荐写一个工具方法安全遍历,避免空指针:while (ex != null && !(ex instanceof SQLException)) { ex = ex.getCause(); }
- PostgreSQL 通常抛出
PSQLException(SQLException子类),MySQL 可能是MySQLTransactionRollbackException或SQLIntegrityConstraintViolationException(JDBC 4.1+)
解析 SQLState 和 errorCode 判断约束类型
SQLException 的 getSQLState() 和 getErrorCode() 比异常消息更稳定、更易程序化识别:
-
PostgreSQL:关注
SQLState前两位(如"23"表示 integrity constraint violation)。常见值:"23505"(唯一约束冲突)、"23503"(外键不存在)、"23514"(检查约束失败) -
MySQL:依赖
errorCode更可靠:1062(重复键)、1452(外键约束失败)、1216(父记录不存在) -
H2 / HSQLDB:参考其文档,H2 常用
errorCode = 23505表示唯一冲突 - 避免直接匹配异常消息字符串(如
"Duplicate entry"),因不同驱动、本地化语言、版本可能导致文本变化
提取具体约束名和字段(可选但实用)
部分数据库驱动会在异常消息中包含约束名或列名,可正则提取增强用户体验:
立即学习“Java免费学习笔记(深入)”;
- PostgreSQL
PSQLException消息常含:"key (username)=(abc) already exists"或"violates unique constraint "uk_user_email"" - MySQL 8.0+ 的
SQLIntegrityConstraintViolationException消息可能含:"Duplicate entry 'test' for key 'users.email_UNIQUE'" - 可用简单正则提取约束名:
Pattern.compile("constraint\s+"([^"]+)"", Pattern.CASE_INSENSITIVE) - 对前端返回结构化错误(如
{"field": "email", "error": "already_exists"}),比泛泛的“数据库保存失败”更友好
在 @ControllerAdvice 中统一处理示例
结合上述逻辑,在全局异常处理器中落地:
- 声明
@ExceptionHandler(DataAccessException.class)捕获所有 Spring 数据异常 - 调用
findRootSQLException(ex)获取真实SQLException - 根据
sqlEx.getSQLState()或sqlEx.getErrorCode()映射业务错误码(如"UNIQUE_VIOLATION") - 构造标准化响应体,包含用户可读提示、机器可解析的错误类型、甚至触发前端特定校验逻辑的字段标识


















