应仅包裹executeUpdate()等实际执行SQL的原子操作;连接初始化、SQL拼接等应在前期独立校验;捕获SQLException后优先用getSQLState()(如"23000")判别业务异常,再结合getErrorCode()细化处理;事务失败后必须rollback()才能继续使用Connection;try-with-resources需警惕close()抛出的压制异常。

SQL更新失败时,try-catch到底该包哪一层?
别把整个数据库连接初始化、查询构造、结果处理全塞进一个try块里。更新失败的根源往往在语义层(比如违反唯一约束)或执行层(比如连接中断),混在一起捕获会导致错误定位失真。
只包裹真正可能抛异常的原子操作:即executeUpdate()(Java)、cursor.execute()(Python)、mysqli_query()(PHP)这类实际触发票据的调用。连接建立、SQL拼接、参数绑定这些环节该报错就该提前报,不该靠更新时的catch兜底。
- Java JDBC 示例中,
conn.prepareStatement(sql)一般不抛SQL异常,但ps.executeUpdate()会抛SQLException - Python
sqlite3下,cursor.execute()可能直接 raisesqlite3.IntegrityError,不是所有异常都得等commit()才暴露 - 如果用了ORM(如MyBatis、Django ORM),异常通常发生在
save()或update()方法调用点,不是filter().update()的链式调用中间
捕获SQLException后,怎么区分是业务冲突还是系统故障?
硬判e.getMessage().contains("duplicate")这种写法脆弱又危险——不同驱动返回消息格式不一致,MySQL 8.0 和 PostgreSQL 的报错文本完全不同,连空格都可能变。
靠谱做法是查SQLState码或原生错误码:SQLState是跨数据库的标准五位码(如"23000"代表完整性约束违规),而getErrorCode()是厂商私有码(MySQL 的1062、PostgreSQL 的23505)。
- 优先用
e.getSQLState()判断大类:比如"23000"基本可认定为唯一键/外键冲突,属于预期中的业务异常 - 再结合
e.getErrorCode()做细粒度分支:MySQL1062是重复键,1205是死锁,需不同重试策略 - 不要忽略
SQLException.getNextException()——批量更新失败时,它可能链着多个子异常
事务里更新失败,catch住之后能继续用同一个Connection吗?
不能。一旦executeUpdate()抛出SQLException,当前事务状态已损坏,JDBC 规范要求后续操作必须先rollback(),否则再调commit()会直接抛SQLException("No transaction is currently active")或更隐蔽的静默失败。
更麻烦的是某些数据库(如 PostgreSQL)在语句级错误后会自动进入“failed transaction”状态,不rollback()就卡死,连新查询都拒绝。
- 必须在
catch块里显式调用conn.rollback(),哪怕你打算丢弃这个连接 - 别在
catch里试图conn.commit()——它大概率失败,且掩盖了原始错误 - 如果用连接池(如HikariCP),记得
rollback()后正常close(),池子会负责回收或重建连接
为什么try-with-resources和catch一起用容易漏掉资源泄漏?
因为try-with-resources的close()可能抛新异常,把原始的SQL异常盖掉。比如更新失败后,PreparedStatement.close()又因网络断开抛SQLException,你catch的就变成关闭异常,而不是更新失败本身。
这不是理论风险——MySQL 驱动在连接已断时调close()确实会触发二次异常,而且堆栈里原始错误信息被吞掉。
- 要么放弃
try-with-resources,手动在finally里close()并忽略关闭异常(if (ps != null) try { ps.close(); } catch (SQLException ignored) {}) - 要么保留
try-with-resources,但在catch里检查e.getSuppressed()数组,里面可能藏着被压制的关闭异常 - 永远别在
close()逻辑里做任何依赖数据库状态的操作(比如记录日志到DB),它本就不该失败
事务边界、异常类型识别、资源清理顺序——这三件事串起来才叫“优雅”,单靠套个try-catch只是把错误藏得更深。










