捕获SQLException需分层识别错误类型、精准响应、安全释放资源并隔离敏感信息;必须用try-with-resources自动关闭Connection等资源,按SQLState和errorCode分类处理,遍历异常链提取根因,事务中须显式回滚。

Java 中捕获并处理 SQLException 不能只靠 try-catch 包住 executeUpdate() 就完事,关键在于资源安全、错误归因准确、响应有区分、数据一致有保障。
用 try-with-resources 自动关闭所有资源
Connection、PreparedStatement、ResultSet 都实现了 AutoCloseable,必须放进 try() 括号里,由 JVM 确保异常或正常执行后都关闭。
- 手动 close 容易遗漏,尤其在多重嵌套或异常跳转路径下,容易导致连接池耗尽
- 不要在 try-with-resources 内再套一层 try 处理 ResultSet —— 扁平结构更可靠
- 示例写法:
PreparedStatement ps = conn.prepareStatement("INSERT INTO users(name) VALUES (?)")) {
ps.setString(1, "Alice");
ps.executeUpdate();
} catch (SQLException e) {
// 处理逻辑
}
按 SQLState 和 errorCode 分类响应,不碰 getMessage()
SQLState 是五位标准码(如 "23000" 表示约束冲突),errorCode 是厂商码(如 MySQL 1062、Oracle 1),二者结合才能准确定义问题类型。
- "08xxx"(如 "08001")→ 连接失败类:可重试或降级提示“服务暂时不可用”
- "23xxx"(如 "23000")→ 数据约束违规:映射为业务语义,如“手机号已被注册”
- errorCode == 1205(SQL Server)或 60(Oracle)→ 死锁:记录日志 + 自动重试一次
- 绝对避免用 e.getMessage().contains("Duplicate") 或正则匹配 "ORA-" —— 日志截断、中间件包装、多语言环境都会让它失效
遍历异常链,不漏掉批量操作中的多个失败
执行 batch 操作时,一个 SQLException 可能封装多个子异常。只处理首层会丢失关键错误点。
立即学习“Java免费学习笔记(深入)”;
- 用 for 循环遍历异常链:
for (Throwable t : e) {
if (t instanceof SQLException se) {
System.err.println("SQLState: " + se.getSQLState());
System.err.println("Code: " + se.getErrorCode());
}
} - MyBatis/Hibernate 封装后,原始 SQLException 常藏在 getCause() 多层之下,需递归提取
事务中异常必须显式回滚,且 DAO 层不抛出 SQLException
开启事务后(setAutoCommit(false)),一旦发生 SQLException,必须立刻 rollback,否则部分更新会提交,破坏一致性。
- 回滚后不要吞掉异常 —— 应包装为自定义业务异常(如 DuplicateKeyException、DeadlockException)向上抛出
- Service 层看到的是语义清晰的异常,不是 JDBC 底层细节;DAO 层是异常转化的边界
- 生产环境严禁把 SQLException.getMessage() 直接返回前端,防止泄露表名、字段、数据库类型等敏感信息


















