推荐将SQLDataException包装为有业务语义的运行时异常(如DataValidationException),在DAO/Service层捕获并转换,保留cause和SQLState等诊断信息,结合前置校验与日志记录,避免泄露JDBC异常至controller层。

直接将 SQLDataException 包装成自定义的运行时异常(如 BusinessRuntimeException)并抛出,是最常用且推荐的做法。
明确异常语义,封装为业务异常
SQLDataException 属于 JDBC 规范定义的受检异常,表示数据相关错误(如类型转换失败、数值溢出、空值约束违反等)。它本身不体现业务含义,也不便于上层统一处理。应将其转为有业务语义的运行时异常:
- 定义一个继承
RuntimeException的异常类,例如DataValidationException或InvalidOrderAmountException - 构造时传入原始异常作为 cause,保留栈信息和错误细节
- 可在构造中提取
SQLDataException.getSQLState()或getErrorCode()做分类判断,增强可诊断性
在 DAO 或 Service 层做转换,避免向上暴露 JDBC 异常
不要让 SQLDataException 泄露到 service 外部或 controller 层。典型做法是在数据访问逻辑中捕获并转换:
try {
// 执行 PreparedStatement.execute() 或 ResultSet.getObject()
} catch (SQLDataException e) {
throw new DataValidationException("订单金额超出系统支持范围", e);
}
如果使用 Spring JDBC 或 MyBatis,也可借助 @Repository + SQLExceptionTranslator 自动完成部分转换,但对 SQLDataException 仍建议显式处理,确保业务语义准确。
立即学习“Java免费学习笔记(深入)”;
结合校验逻辑,前置拦截减少运行时异常
很多 SQLDataException 实际源于数据不合规(如传入超长字符串、非法日期格式、空值写入非空字段)。应在进入 SQL 执行前做轻量校验:
- 用 Bean Validation(
@NotNull,@Size,@DecimalMax)约束 DTO/Entity 字段 - 对关键数值字段做范围检查(如金额 ≤ 99999999.99)
- 数据库字段类型与 Java 类型映射要一致(如
DECIMAL(10,2)对应BigDecimal,避免用double)
日志记录原始异常,便于排查根因
转换异常时别忽略日志。至少记录:SQLState、errorCode、SQL 语句片段(脱敏)、参数快照:
log.warn("SQL data error on insert order: state={}, code={}, sql={}",
e.getSQLState(), e.getErrorCode(), maskedSql, e);
这样既对外返回清晰的业务异常,又保留下游可追溯的原始线索。


















