if-else仅用于轻量前置判断,如租户前缀、业务类型、时间粗筛及兜底;真实路由由AbstractRoutingDataSource根据返回key从targetDataSources中选择数据源。

在 Java 中用 if-else 实现基于数据库分表规则的动态数据源切换路由,核心不是靠 if-else 做最终路由决策,而是用它做**简单、明确、边界清晰的前置判断逻辑**,配合 Spring 的 AbstractRoutingDataSource 完成实际的数据源选择。直接用 if-else 硬编码所有路由分支既不可维护,也违背开闭原则。
明确 if-else 的合理使用场景
if-else 适合处理以下几类轻量、确定性高的路由判断:
- 按租户 ID 前缀快速分流:如租户 ID 以 "CN" 开头走 ds-cn,以 "US" 开头走 ds-us;
- 按业务类型硬编码映射:如 order 表固定走 ds-order,log 表固定走 ds-log(非分表,而是物理隔离);
- 按时间范围做冷热分离初判:如创建时间 > 2024-01-01 走主库,否则走历史库(仅作一级粗筛);
- 兜底策略判断:当分表键为空、非法或未命中任何规则时,统一 fallback 到默认数据源。
结合 AbstractRoutingDataSource 实现真实路由
Spring 提供的 AbstractRoutingDataSource 是标准解法。你需要:
- 继承它,重写 determineCurrentLookupKey() 方法;
- 在该方法中,用 if-else 处理上述轻量判断逻辑,返回一个 key(如 "ds-order-202409");
- 将所有数据源(如 ds-order-202409、ds-order-202408)预先注册到父类的 targetDataSources Map 中;
- 确保 key 与 Map 中的 key 完全一致(大小写、连字符等需严格匹配)。
避免常见陷阱
if-else 写得越多越危险,务必守住三条线:
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
立即学习“Java免费学习笔记(深入)”;
- 不参与分表键哈希/取模计算:比如 user_id % 16 得到分表编号,这种应封装为独立工具类,不在 if-else 里硬写;
- 不嵌套超过 3 层:一旦出现 if (a) { if (b) { if (c) { ... } } },说明逻辑已超载,应提取为策略对象或查表配置;
- 不混用运行时参数和静态配置:例如把数据库连接串拼在 if 分支里,会导致无法热更新、难测试、易出错。
一个精简可行的代码示意
假设按订单创建年月分库(order_202409、order_202408…),且只对特定租户启用:
@Override
protected Object determineCurrentLookupKey() {
OrderContext context = OrderContextHolder.get();
String tenantId = context.getTenantId();
LocalDateTime createTime = context.getCreateTime();
// if-else 仅做快速守门员
if ("vip-prod".equals(tenantId)) {
return "ds-order-" + createTime.format(DateTimeFormatter.ofPattern("yyyyMM"));
}
if ("demo".equals(tenantId)) {
return "ds-order-demo";
}
if (createTime == null) {
return "ds-order-default";
}
// 兜底
return "ds-order-default";
}
真正的分表逻辑(如根据 order_no 解析月份、支持跨月查询)应放在 DAO 层或 MyBatis 插件中,而不是塞进这个方法。


















