Mybatis数据源切换


方案一:配置文件 + 硬编码切换(基础版)

你提到的第一种方式,是最基础、最直观的实现。它的核心流程是:

  1. 配置 :在 application.ymlmybatis-config.xml 中定义多个数据源(如 master, slave1, slave2)。
  2. 持有 :在代码中通过一个工具类(比如使用 ThreadLocal)持有当前线程要使用的数据源 key。
  3. 切换 :在业务代码中,手动调用工具类的方法(如 DataSourceContextHolder.setDataSource("slave1"))来切换。
维度 评价
优点 简单直接,容易理解,没有黑盒。适合业务逻辑简单、数据源切换点非常明确的场景。
缺点 切换逻辑和业务代码耦合,侵入性强。如果忘记在方法结束时清除上下文,可能导致后续逻辑使用了错误的数据源,产生难以追踪的 Bug。

方案二:AOP 切面编程(解耦版)

这是对方案一的优化,也是目前最常用的实践。它通过 Spring AOP 将"切换"这个动作从业务代码中抽离出来。

核心逻辑是:定义一个注解(如 @DataSource("slave1")),然后编写一个切面,拦截所有带有该注解的方法,在方法执行前解析注解并将数据源标识设置到 ThreadLocal 中,方法执行后在 @After 中清除标识。

维度 评价
优点 切换逻辑与业务解耦,代码更干净。只需在类或方法上加一个注解就能切换,可维护性高,是生产环境最推荐的实现方式。
缺点 需要编写切面类和注解,有一定学习成本。对 AOP 的作用范围(如 @Transactional 的优先级)需要特别注意,避免事务开启后再切换数据源导致切换失效。

方案三:继承 AbstractRoutingDataSource(动态路由版)

这是 Spring 提供的官方扩展点,也是实现方案一和方案二的底层基石。

AbstractRoutingDataSource 本身是一个数据源,它内部管理着一个 Map<Object, Object> 来存放真实数据源,并根据 determineCurrentLookupKey() 方法返回的 key 来决定路由到哪个真实数据源。我们只需要实现这个方法来返回我们通过 ThreadLocal 设置的 key 即可。

很多资料和第三方库(包括 MyBatis-Plus 的 dynamic-datasource)的底层,本质上都是对 AbstractRoutingDataSource 的封装和增强。

方案四:使用第三方库(成熟版)

你提到的 Druid(数据库连接池)和 HikariCP(连接池),它们本身并不直接提供数据源切换功能,但它们是这个方案中不可或缺的"高性能连接池"组件。

真正用于动态切换数据源的明星第三方库,是 MyBatis-Plus 生态下的 dynamic-datasource

维度 评价
优点 开箱即用,功能强大 。支持数据源分组、密码解密、多主多从、敏感信息加密、集成 Seata 分布式事务等。只需引入依赖并在 application.yml 中配置,然后直接在方法上使用 @DS("slave1") 注解即可。
缺点 引入了一个外部依赖。如果项目没有使用 MyBatis-Plus,需要额外集成。对于非常简单的需求,可能显得有点"重"。

📊 最终选择指南

你的情况 推荐方案
项目简单,切换点极少(1-2个),且未来不会扩展 方案一(硬编码)即可。
标准企业级应用,使用 Spring Boot + MyBatis 方案二(自研 AOP + AbstractRoutingDataSource) 是主流选择,平衡了灵活性与可控性。
已经使用 MyBatis-Plus,或希望最大程度简化配置 方案四(dynamic-datasource 插件) 是最高效的选择。
有非常复杂的动态路由规则(如根据 userId 哈希取模) 方案三(继承 AbstractRoutingDataSource 是最灵活、最底层的方式,可以在 determineCurrentLookupKey() 中实现任意逻辑。

对于大多数项目,方案二(AOP + 注解 + AbstractRoutingDataSource)方案四(MyBatis-Plus 的 dynamic-datasource) 是最值得考虑的。

另外补充一点,无论选择哪种方案,都要注意:如果在 Spring 事务(@Transactional)中切换数据源,必须在事务开启之前进行 。因为事务管理器在开启时就已经确定了要使用的数据源,之后的切换将不会生效。如果需要在事务中切换数据源,可以考虑使用 @Transactional(propagation = Propagation.REQUIRES_NEW) 开启一个新的事务,或将切换逻辑放在一个没有事务的方法中调用。

相关推荐
Ethan01071 天前
pt-deadlock-logger 检测死锁
mysql
Ethan01071 天前
数据库死锁-应用层重试
mysql
宠友信息1 天前
消息序号如何保证即时通讯源码聊天记录稳定加载
java·spring boot·redis·python·mysql·uni-app
笨蛋不要掉眼泪2 天前
MySQL架构揭秘:慢查询日志详解
数据库·mysql·架构
️学习的小王2 天前
MySQL 实战:从建表到索引管理的完整指南
数据库·mysql·oracle
宠友信息2 天前
MySQL复合索引与Druid优化仿小红书源码个人主页查询链路
数据库·spring boot·websocket·mysql·uni-app
minji...2 天前
MySQL数据库 (十九) MySQL图像化界面,MySQL连接池
数据库·mysql·navicat·mysql workbench·mysql连接池·数据库图形化界面
文档搬运工2 天前
Innodb Cluster安装
mysql
想躺平的小羊2 天前
MySQL中LAST_DAY函数用法
数据库·mysql