SQLException异常链结构因驱动而异:PostgreSQL驱动链较完整,需遍历getNextException();MySQL驱动链弱,应优先解析getErrorCode()和getSQLState();连接池(如HikariCP)会包装异常并清空nextException,需递归getCause();Oracle/SQL Server倾向用子类表达语义,但需结合SQLState校验。

SQLException异常链的结构因驱动而异
SQLException本身支持链式异常(getNextException()),但不同数据库驱动对异常链的填充策略差别很大。这直接影响你能否完整捕获批量操作、事务回滚或连接池包装过程中的全部错误上下文。
PostgreSQL驱动:异常链较完整,常含原始协议错误
pgjdbc 通常会在主SQLException下挂载底层网络或协议异常(如SocketException、SSLHandshakeException),尤其是连接失败或查询中断时。调用 e.getNextException() 可能返回 1–2 层真实原因,比如:
- 主异常:SQLState="08006"(连接已关闭),ErrorCode=0
- 下一个异常:java.net.SocketTimeoutException: Read timed out
建议遍历整个链,不只看首层——否则可能错过超时或证书失效等根本原因。
MySQL驱动(Connector/J):异常链较弱,依赖ErrorCode和SQLState组合判错
旧版驱动(8.0之前)基本不设置nextException,所有信息都压在主异常上;新版虽支持,但多数场景仍为空。例如批量插入失败时:
立即学习“Java免费学习笔记(深入)”;
- 不会像PostgreSQL那样抛出BatchUpdateException并带多个子异常
- 而是返回单个SQLException,ErrorCode=1062(重复键),SQLState="23000",getMessage()含具体字段名
此时不能依赖getNextException(),应优先解析getErrorCode()和getSQLState(),再结合日志上下文定位哪条语句出错。
HikariCP等连接池会包装异常,破坏原始链
连接池获取连接失败时(如连接耗尽、验证SQL执行失败),往往把原始SQLException包装成新的SQLException,原异常变成cause,但getNextException()被清空。例如:
- HikariPool throws SQLException with message "Connection is not available"
- getCause() 是底层驱动抛出的真实异常(如MySQL的CommunicationsException)
- getNextException() 返回 null,即使原始驱动本有链
排查时需递归调用 getCause(),直到拿到 JDBC 驱动级异常,再从中提取 getSQLState() 和 getErrorCode()。
Oracle与SQL Server驱动:倾向使用子类而非异常链
它们更常用特定子类表达语义(如SQLTimeoutException、SQLIntegrityConstraintViolationException),这些子类仍是SQLException的子类,但本身不携带nextException。优势是类型明确,可直接catch子类:
- catch (SQLTimeoutException e) → 做重试或降级
- catch (SQLIntegrityConstraintViolationException e) → 转用户提示
但要注意:不是所有驱动都严格创建子类实例,有些仍返回通用SQLException,所以不能仅靠instanceof判断,仍需校验SQLState前缀(如"40001"表示死锁)。



















