java mybatis 多数据源动态切换需依赖 abstractroutingdatasource + threadlocal + aop + @ds 注解四者协同,严格保证数据源 key 一致,事务内无法切换,mybatis-plus 需注解在 mapper 方法且确保 sqlsessionfactory 指向 dynamicdatasource。

Java MyBatis 在多数据源场景下动态切换数据源,核心在于**不修改业务代码逻辑的前提下,让 SQL 执行自动路由到指定数据源**。这不是靠 MyBatis 自身识别注解就能完成的,而是需要 Spring 的 AbstractRoutingDataSource + 线程上下文(ThreadLocal)+ AOP 切面协同工作。
关键组件必须配齐
四个角色缺一不可:
-
DynamicDataSource:继承
AbstractRoutingDataSource,重写determineCurrentLookupKey()方法,它每次从ThreadLocal中读取当前线程绑定的数据源 key -
DataSourceContextHolder:封装
ThreadLocal<string></string>,提供setDataSource()、getDataSource()和clearDataSource(),确保线程隔离与资源清理 -
@DS 注解:自定义注解(如
@Target({METHOD, TYPE})),标记在 Mapper 接口方法或 Service 方法上,声明要使用的数据源名称(如"slave"或"ds_order") -
AOP 切面:拦截带
@DS的方法,在执行前调用DataSourceContextHolder.setDataSource();方法结束后调用clearDataSource(),防止线程复用污染
配置时注意 key 必须严格一致
你在 DynamicDataSource 构造时注册的目标数据源 map(targetDataSources)中,每个 key(比如 "master"、"slave")必须和 @DS("slave") 中的字符串、以及 determineCurrentLookupKey() 返回值完全相同——包括大小写、空格、引号。任何不一致都会导致 fallback 到默认数据源,若未设置默认源则直接抛异常:IllegalStateException: DataSource router no longer supports null keys。
事务内切换会失效,必须拆开处理
Spring 的 @Transactional 在方法入口就获取并绑定连接,整个事务生命周期内复用该连接。即使你在事务方法里调另一个加了 @DS("slave") 的 Mapper 方法,AOP 虽然触发了,但 SqlSession 已锁定初始数据源,不会重新取连接。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
解决方式只有两种:
- 把跨数据源操作拆成独立方法,并标注
@Transactional(propagation = Propagation.REQUIRES_NEW) - 避免在单个事务中混用多个数据源——这是设计层面的约束,不是 bug
MyBatis-Plus 用户特别注意
如果你用的是 MyBatis-Plus,@DS 注解需加在 Mapper 接口方法上(不是实现类或 XML),且切面 pointcut 建议写为 @Within(com.example.mapper..*) 或明确指定 Mapper 包路径。否则 AOP 可能拦不到代理方法,注解形同虚设。
同时确认 SqlSessionFactory 使用的是你配置的 DynamicDataSource,而不是某个具体数据源 Bean;否则所有操作仍走固定库。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










