java中jdbc多数据源动态切换需基于spring的abstractroutingdatasource,配合threadlocal上下文与aop注解实现;核心组件包括多个真实数据源、继承abstractroutingdatasource的路由数据源及threadlocal持有的上下文管理器;通过@targetdatasource注解与切面在方法执行前设置并清理数据源标识,且事务中切换须在事务开启前完成,避免跨方法切换或private方法失效。

Java 中用 JDBC 实现多数据源动态切换,核心不是直接操作 Connection,而是让整个数据访问链路能根据运行时条件自动选库。纯 JDBC 本身不提供路由能力,必须结合 Spring 的 AbstractRoutingDataSource 机制,并通过 ThreadLocal 绑定上下文,才能实现“一次配置、按需切换”的效果。
关键组件要配齐
需要三个基础角色协同工作:
-
多个真实数据源(DataSource):比如
masterDataSource和slaveDataSource,各自独立配置 URL、用户名、驱动等,作为底层连接池。 -
一个路由数据源(DynamicRoutingDataSource):继承
AbstractRoutingDataSource,重写determineCurrentLookupKey()方法,从 ThreadLocal 中读取当前线程标记的数据源 key(如 "master" 或 "slave")。 -
上下文持有器(DataSourceContextHolder):用
ThreadLocal<string></string>存储当前线程应使用的数据源标识,提供set、get、clear方法。
切换逻辑靠 AOP 拦截方法注解
不能每次查库都手动调 setDataSourceType("slave"),那样业务代码就散了。推荐做法是定义一个自定义注解:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TargetDataSource {
String value();
}
再写一个切面,在目标方法执行前设置 key,执行后清理:
- 遇到
@TargetDataSource("slave"),就调DataSourceContextHolder.setDataSourceType("slave"); - 方法结束时,必须调
DataSourceContextHolder.clearDataSourceType(),避免线程复用导致污染。
Spring 配置不能漏掉细节
在配置类中,要把所有真实数据源注入到路由数据源的 targetDataSources 映射里,并指定默认数据源:
- 用
Map<object datasource></object>把 "master" →masterDataSource、"slave" →slaveDataSource关联起来; - 把路由数据源声明为
@Primary,确保 MyBatis 的SqlSessionFactory或 JdbcTemplate 默认使用它; - MyBatis 的
sqlSessionFactory必须引用这个路由数据源,否则切换无效。
事务场景要特别小心
如果方法加了 @Transactional,切换必须在事务开启前完成。因为 Spring 的事务管理器(DataSourceTransactionManager)会在事务开始时锁定数据源。常见坑点:
- 注解写在 private 方法上——AOP 不生效,切换失败;
- 同一个事务内跨方法调用,且子方法带不同
@TargetDataSource——会抛异常,Spring 不允许事务中切换数据源; - 没配置好事务管理器对应的
dataSource,导致事务和查询走的不是同一个库。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










