MyBatis动态数据源切换的完整方案

 更新时间:2025年08月26日 09:27:26   作者:墨瑾轩  
在微服务架构和分布式系统中,动态数据源是应对多环境切换、读写分离、多租户等复杂场景的核心工具,MyBatis 与 Spring 结合后,可以灵活实现数据源的动态切换,以下是实现 MyBatis 动态数据源的完整方案,需要的朋友可以参考下

在微服务架构和分布式系统中,动态数据源是应对多环境切换、读写分离、多租户等复杂场景的核心工具。MyBatis 与 Spring 结合后,通过 AbstractRoutingDataSource线程上下文管理ThreadLocal),可以灵活实现数据源的动态切换。以下是实现 MyBatis 动态数据源的完整方案,涵盖核心步骤、代码示例和最佳实践。

1. 核心概念

  • 动态数据源:根据业务需求自动选择不同的数据库连接(如主库、从库、测试库)。
  • AbstractRoutingDataSource:Spring 提供的抽象类,负责动态决定当前使用哪个数据源。
  • ThreadLocal:线程安全的上下文管理工具,保存当前线程的数据源标识(如 masterslave)。

2. 实现步骤

(1) 配置多个数据源

application.ymlapplication.properties 中定义多个数据源配置:

spring:
  datasource:
    master:
      url: jdbc:mysql://localhost:3306/master_db
      username: root
      password: root
      driver-class-name: com.mysql.cj.jdbc.Driver
    slave:
      url: jdbc:mysql://localhost:3306/slave_db
      username: root
      password: root
      driver-class-name: com.mysql.cj.jdbc.Driver

(2) 创建动态数据源类

继承 AbstractRoutingDataSource,重写 determineCurrentLookupKey 方法:

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

public class DynamicDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        // 从线程上下文中获取当前数据源标识
        return DataSourceContextHolder.getDataSource();
    }
}

(3) 数据源上下文管理

使用 ThreadLocal 保存当前线程的数据源标识:

public class DataSourceContextHolder {
    private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();

    public static void setDataSource(String dataSource) {
        contextHolder.set(dataSource);
    }

    public static String getDataSource() {
        return contextHolder.get();
    }

    public static void clearDataSource() {
        contextHolder.remove();
    }
}

(4) 配置动态数据源 Bean

在 Spring 配置类中定义动态数据源,并绑定多个数据源:

@Configuration
public class DataSourceConfig {

    @Bean(name = "masterDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.master")
    public DataSource masterDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean(name = "slaveDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.slave")
    public DataSource slaveDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean
    @Primary
    public DataSource dynamicDataSource(
        @Qualifier("masterDataSource") DataSource masterDataSource,
        @Qualifier("slaveDataSource") DataSource slaveDataSource) {
        
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put("master", masterDataSource);
        targetDataSources.put("slave", slaveDataSource);

        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        dynamicDataSource.setTargetDataSources(targetDataSources);
        dynamicDataSource.setDefaultTargetDataSource(masterDataSource); // 默认使用主数据源
        return dynamicDataSource;
    }
}

(5) 整合 MyBatis

配置 SqlSessionFactoryMapperScannerConfigurer,确保使用动态数据源:

@Configuration
@MapperScan(basePackages = "com.example.mapper")
public class MyBatisConfig {

    @Autowired
    private DataSource dynamicDataSource;

    @Bean
    public SqlSessionFactory sqlSessionFactory() throws Exception {
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setDataSource(dynamicDataSource);
        return factoryBean.getObject();
    }
}

(6) 动态切换数据源

在业务代码中通过 DataSourceContextHolder 设置数据源标识:

// 切换到从数据源(读操作)
DataSourceContextHolder.setDataSource("slave");
try {
    // 执行查询操作
    List<User> users = userMapper.selectAll();
} finally {
    // 清除线程上下文
    DataSourceContextHolder.clearDataSource();
}

// 切换到主数据源(写操作)
DataSourceContextHolder.setDataSource("master");
try {
    userMapper.insert(user);
} finally {
    DataSourceContextHolder.clearDataSource();
}

(7) 事务管理

为每个数据源配置独立的事务管理器,并通过 @Transactional 注解指定事务管理器:

@Configuration
@EnableTransactionManagement
public class TransactionConfig {

    @Bean(name = "masterTransactionManager")
    public PlatformTransactionManager masterTransactionManager(
        @Qualifier("masterDataSource") DataSource masterDataSource) {
        return new DataSourceTransactionManager(masterDataSource);
    }

    @Bean(name = "slaveTransactionManager")
    public PlatformTransactionManager slaveTransactionManager(
        @Qualifier("slaveDataSource") DataSource slaveDataSource) {
        return new DataSourceTransactionManager(slaveDataSource);
    }
}

// 在 Service 层指定事务管理器
@Service
public class UserService {

    @Transactional("masterTransactionManager")
    public void insertUser(User user) {
        userMapper.insert(user);
    }

    @Transactional("slaveTransactionManager")
    public List<User> selectAllUsers() {
        return userMapper.selectAll();
    }
}

3. 使用场景

读写分离

  • 写操作(INSERT/UPDATE/DELETE)使用主库,读操作(SELECT)使用从库。
  • 示例:订单写入主库,订单查询从库。

多环境切换

  • 开发环境、测试环境、生产环境分别绑定不同的数据源。
  • 示例:通过配置文件切换不同数据库地址。

多租户隔离

  • 每个租户使用独立的数据源,通过租户标识动态切换。
  • 示例:企业级 SaaS 系统,不同企业访问不同数据库。

4. 注意事项

线程安全

  • 确保 ThreadLocal 在异步任务中传递数据源标识(如使用 InheritableThreadLocalTransmittableThreadLocal)。

事务一致性

  • 跨数据源的事务需谨慎处理,避免脏读或数据不一致。可使用分布式事务框架(如 Seata)。

性能优化

  • 避免频繁切换数据源,合理设计读写分离策略。

异常处理

  • 捕获数据源切换异常,提供回退机制(如默认使用主数据源)。

5. 总结

特性实现方式
动态切换AbstractRoutingDataSource + ThreadLocal
读写分离通过业务逻辑判断操作类型,设置 master/slave
多环境支持通过配置文件区分不同环境的数据源
事务管理为每个数据源配置独立的 PlatformTransactionManager
线程安全使用 ThreadLocal 保存上下文,异步场景需传递上下文

通过以上方案,MyBatis 动态数据源可以灵活适配多种业务场景,成为多环境切换的“神器”。结合 Spring 的强大生态,开发者可以轻松实现高可用、高性能的分布式系统。

以上就是MyBatis动态数据源切换的完整方案的详细内容,更多关于MyBatis动态数据源切换的资料请关注脚本之家其它相关文章!

相关文章

  • 全面详解java代码重构与设计模式

    全面详解java代码重构与设计模式

    这篇文章主要为大家介绍了全面详解java代码重构与设计模式的全面详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-06-06
  • SpringBoot实现多租户系统架构的5种设计方案介绍

    SpringBoot实现多租户系统架构的5种设计方案介绍

    多租户(Multi-tenancy)是一种软件架构模式,允许单个应用实例服务于多个客户(租户),同时保持租户数据的隔离性和安全性,本文分享了SpringBoot环境下实现多租户系统的5种架构设计方案,需要的可以参考一下
    2025-05-05
  • 关于String转Json的几种方式

    关于String转Json的几种方式

    这篇文章主要介绍了关于String转Json的几种方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-12-12
  • Java设计模式笔记之Builder模式

    Java设计模式笔记之Builder模式

    建造者模式是日常开发中比较常见的设计模式,它的主要作用就是将复杂事物创建的过程抽象出来,该抽象的不同实现方式不同,创建出的对象也不同,这篇文章主要给大家介绍了关于Java设计模式笔记之Builder模式的相关资料,需要的朋友可以参考下
    2022-03-03
  • 基于JavaMail实现邮件发送

    基于JavaMail实现邮件发送

    这篇文章主要为大家详细介绍了基于JavaMail实现邮件发送功能,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-03-03
  • java 使用简单的demo实例告诉你优化算法的强大

    java 使用简单的demo实例告诉你优化算法的强大

    本篇文章介绍了,在java中使用简单的demo实例告诉你优化算法的强大。需要的朋友参考下
    2013-05-05
  • mybatisplus之Wrappers.ne踩坑记录解决

    mybatisplus之Wrappers.ne踩坑记录解决

    这篇文章主要为大家介绍了mybatisplus之Wrappers.ne踩坑记录解决,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-05-05
  • Java连接Redis的两种方式

    Java连接Redis的两种方式

    Redis 是一种高性能的键值存储数据库,广泛应用于缓存、消息队列、会话存储等场景,Java 作为一门广泛使用的编程语言,提供了多种方式来连接和操作 Redis,本文将介绍两种常用的 Java 连接 Redis 的方式,需要的朋友可以参考下
    2025-03-03
  • Mybatis-plus对单表操作的封装实现

    Mybatis-plus对单表操作的封装实现

    本文详细介绍了MyBatis-Plus单表操作,包括自定义SQL、逻辑删除、乐观锁、全局拦截器和代码生成器等,具有一定的参考价值,感兴趣的可以了解一下
    2024-12-12
  • java与C 代码运行效率的对比(整理)

    java与C 代码运行效率的对比(整理)

    最近和朋友无意间讨论起了 有关java 和C 的 效率问题, (我是java 推介者, 他是 c 语言推介者, 他做的是嵌入式)故,想通过网络查询一下, 总结一下,两者到底效率如何,其有何差异,原因又是啥?各种优势有在何处?
    2021-04-04

最新评论