Java中JDBC读写分离路由的核心是根据SQL类型动态选择数据源:SELECT或含/+ READ /注释走从库,INSERT/UPDATE/DELETE等或含/+ WRITE /走主库,通过RoutingDataSource封装getConnection()实现自动路由,并需事务内强制使用主库以保证一致性。

Java 中用 JDBC 实现简单的读写分离路由,核心是**在应用层根据 SQL 类型(SELECT vs INSERT/UPDATE/DELETE)动态选择数据源**,不依赖中间件或框架,适合轻量级场景。关键在于拦截 SQL、识别读写意图,并路由到对应的数据源连接。
1. 定义读写数据源连接池
准备两个独立的 HikariCP(或其他)连接池:一个指向主库(支持读写),一个指向从库(只读)。
- 主数据源(
writeDataSource):用于所有写操作和部分强一致性读 - 从数据源(
readDataSource):用于普通查询,可配置多个实现负载均衡
注意:确保从库已开启复制且延迟可控,否则读取可能不一致。
2. 实现基于 SQL 前缀的简单路由判断
不需要完整 SQL 解析,只需检查语句开头是否为 SELECT(忽略大小写和空格/注释):
立即学习“Java免费学习笔记(深入)”;
你是 TypeScript 项目架构专家,专注于脚手架搭建生产就绪的 Node.js 和前端应用。生成包含现代工具链(pnpm、Vite、Next.js)、类型
- 以
SELECT开头 → 路由到readDataSource - 以
INSERT、UPDATE、DELETE、REPLACE、CALL等开头 → 路由到writeDataSource - 支持
/*+ read */或/*+ write */注释强制指定(增强灵活性)
示例逻辑片段:
String sql = sql.trim().toUpperCase();
if (sql.startsWith("SELECT") || sql.contains("/*+ READ */")) {
return readDataSource.getConnection();
} else if (sql.startsWith("INSERT") || sql.startsWith("UPDATE") ||
sql.startsWith("DELETE") || sql.contains("/*+ WRITE */")) {
return writeDataSource.getConnection();
}
// 默认走主库
return writeDataSource.getConnection();
3. 封装统一的 Connection 获取入口
避免业务代码直接 new DataSource,提供一个 RoutingDataSource 工具类:
- 暴露
getConnection(String sql)方法,自动路由 - 对 PreparedStatement 和 Statement 做适配封装(可选)
- 支持线程局部变量(ThreadLocal)绑定当前连接类型,便于事务内强制走主库(如读-写混合事务)
例如:RoutingDataSource.getConnection("SELECT * FROM user") 返回从库连接;getConnection("UPDATE user SET ...") 返回主库连接。
4. 注意事务与一致性边界
纯 JDBC 场景下没有自动事务感知,需人工约定:
- 开启事务(
connection.setAutoCommit(false))时,后续所有操作必须使用同一连接 → 此时应强制走主库 - 可在工具类中增加
beginTransaction()方法,内部标记当前线程“处于写事务”,后续 getConnection 忽略 SQL 类型,直连主库 - 不建议在事务中混用主从连接,JDBC 不会帮你协调分布式事务
若需更严谨控制,可结合 AOP 或自定义注解(如 @ReadOnly)在方法层面声明意图。


















