MyBatis 多数据源切换依赖 Spring 的 AbstractRoutingDataSource + 自定义注解 + AOP 切面组合;需配置多个 DataSource Bean 并标注 @Primary,继承 AbstractRoutingDataSource 重写 determineCurrentLookupKey() 从 ThreadLocal 获取 key,配合 ThreadLocal 上下文、@DS 注解及 AOP 实现切换,事务内无法动态切换数据源。

MyBatis 本身不支持多数据源切换,真正起作用的是 Spring 的 AbstractRoutingDataSource + 自定义注解 + AOP 切面组合。只加 @DS("xxx") 注解不会生效,它只是个标记,必须配套完整的路由机制才能工作。
配置多个 DataSource Bean
在 Spring Boot 中,需显式声明每个物理数据源为独立的 DataSource Bean,并用 @Qualifier 区分:
- application.yml 中按前缀分开配置,例如
spring.datasource.db1.url和spring.datasource.db2.url - 写一个配置类,用
@ConfigurationProperties绑定不同前缀,分别构建db1DataSource()和db2DataSource() - 确保其中一个加
@Primary,避免 Spring 启动时报 “No qualifying bean of type 'DataSource'”
搭建动态路由数据源 DynamicDataSource
继承 AbstractRoutingDataSource,重写 determineCurrentLookupKey() 方法,让它从 ThreadLocal 取当前 key:
- key 值必须与后续注册到
targetDataSources中的 map key 完全一致(大小写、空格都不能错) - 返回 null 会触发 fallback 到
defaultTargetDataSource;若没配,默认抛IllegalStateException: DataSource router no longer supports null keys - 不要在该方法里查数据库或远程调用,它会在每次
getConnection()时被高频调用
用 ThreadLocal + 自定义注解 + AOP 实现切换
核心三件套缺一不可:
立即学习“Java免费学习笔记(深入)”;
-
上下文管理类:用
ThreadLocal<String>存当前数据源标识,提供set()、get()、clear()方法 -
自定义注解:如
@DS("db2"),标注在 Mapper 接口方法上(不是实现类),方法级优先于类级 -
AOP 切面:切点匹配
@Within(com.example.mapper..*)或具体包,前置通知中读取注解值并调用DataSourceContextHolder.set(...);后置或最终通知中务必调用clear(),防止线程复用污染
事务内切换的注意事项
Spring 事务在方法入口就绑定连接,整个事务周期内复用同一数据源:
- 在
@Transactional方法内部再调用另一个带@DS的 Mapper,AOP 虽然触发了,但 SqlSession 已绑定初始数据源,不会重新获取连接 - 强行切换可能报
Cannot change transaction isolation level after connection has been obtained - 跨库操作必须拆成独立事务,用
@Transactional(propagation = Propagation.REQUIRES_NEW)隔离


















