Java分库分表路由失败时保留原SQL的核心是定义携带SQL的自定义异常类RouteFailureException,并通过异常链(构造函数传入cause)逐层透传,确保各层包装时不丢失originalSql和原始异常,日志及全局处理器中显式输出SQL与cause。

Java中利用异常链在分库分表路由失败时保留原SQL,核心是将原始SQL作为上下文信息嵌入到自定义异常中,并通过异常链(Throwable.initCause() 或构造函数传入 cause)逐层传递,避免关键诊断信息丢失。
定义携带SQL的自定义异常
创建一个继承 RuntimeException(或 SQLException)的异常类,显式保存 SQL 字符串:
public class RouteFailureException extends RuntimeException {
private final String originalSql;
<pre class="brush:php;toolbar:false;">public RouteFailureException(String message, String originalSql) {
super(message);
this.originalSql = originalSql;
}
public RouteFailureException(String message, String originalSql, Throwable cause) {
super(message, cause); // 自动建立异常链
this.originalSql = originalSql;
}
public String getOriginalSql() {
return originalSql;
}}
这样,无论异常被哪层捕获或包装,原始 SQL 都可直接获取,不依赖堆栈解析。
在路由逻辑中主动抛出带SQL的异常
当分库分表中间件(如 ShardingSphere、自研路由模块)无法确定目标库表时,不要仅抛出泛型异常,而应封装原始 SQL:
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
- 从
PreparedStatement或 MyBatis 的MappedStatement中提取绑定前的 SQL 模板(如sqlSource.getBoundSql().getSql()) - 若 SQL 已参数化,优先保留未替换占位符的模板(如
"SELECT * FROM t_order WHERE user_id = ?"),比日志拼接更准确 - 捕获底层路由异常(如
NoRouteFoundException)时,用它作为 cause 构建RouteFailureException
向上透传异常链,避免信息截断
各层调用(DAO → Service → Controller)不应简单 throw new RuntimeException(e),否则原异常和 SQL 会丢失:
- ✅ 正确:用含 cause 的构造器重新包装,SQL 和原始异常均保留
- ❌ 错误:只调用
new RuntimeException("路由失败"),丢弃 cause 和 SQL 字段 - 日志打印时,显式输出
e.getOriginalSql()和e.getCause(),而非仅e.toString()
在全局异常处理器中结构化输出
Spring Boot 的 @ControllerAdvice 可统一处理,返回带 SQL 的错误响应:
@ExceptionHandler(RouteFailureException.class)
public ResponseEntity<map object>> handleRouteFailure(RouteFailureException e) {
Map<string object> body = new HashMap();
body.put("error", "分库分表路由失败");
body.put("sql", e.getOriginalSql());
body.put("detail", e.getCause() != null ? e.getCause().getMessage() : "未知原因");
return ResponseEntity.status(400).body(body);
}</string></map>运维或开发排查时,可立即看到“执行了什么 SQL”以及“为什么没路由成功”,无需翻查多层日志或还原调用栈。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










