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动态数据源切换的资料请关注脚本之家其它相关文章!

相关文章

  • JDK8接口的默认与静态方法-接口与抽象类的区别详解

    JDK8接口的默认与静态方法-接口与抽象类的区别详解

    这篇文章主要介绍了JDK8接口的默认与静态方法-接口与抽象类的区别详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,,需要的朋友可以参考下
    2019-06-06
  • Maven解决jar包版本冲突的4种方法详解

    Maven解决jar包版本冲突的4种方法详解

    这篇文章主要给大家介绍了关于Maven解决jar包版本冲突的4种方法代码,maven工程要导入jar包的坐标,就必须要考虑解决jar包冲突,文中介绍的非常详细,需要的朋友可以参考下
    2023-07-07
  • Spring中的@Transactional注解使用详解

    Spring中的@Transactional注解使用详解

    这篇文章主要介绍了Spring中的@Transactional注解使用详解,@Transactional 是一个注解,用于在 Spring 框架中管理事务的声明式事务管理机制,它可以应用于方法或类级别,并指示方法或类在执行时应该被包装在一个事务中,需要的朋友可以参考下
    2024-01-01
  • 如何用Dos命令运行Java版HelloWorld你知道吗

    如何用Dos命令运行Java版HelloWorld你知道吗

    这篇文章主要介绍了在dos窗口中编译和运行java文件的方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-08-08
  • 关于Java三大特性之多态的总结

    关于Java三大特性之多态的总结

    这篇文章主要介绍了关于Java三大特性之多态的总结,内容详细,涉及多态的定义,存在条件,好处,分类及实现方式等相关内容,具有一定参考价值,需要的朋友可以了解下。
    2017-11-11
  • 2021年最新Redis面试题汇总(4)

    2021年最新Redis面试题汇总(4)

    在程序员面试过程中redis相关的知识是常被问到的话题。这篇文章主要介绍了几道Redis面试题,整理一下分享给大家,感兴趣的小伙伴们可以参考一下
    2021-07-07
  • Java中的线程生命周期核心概念

    Java中的线程生命周期核心概念

    这篇文章主要介绍了Java中的线程生命周期核心概念,通过使用一个快速的图解展开文章内容,需要的小伙伴可以参考一下
    2022-06-06
  • Java面向对象特性深入刨析封装

    Java面向对象特性深入刨析封装

    封装是一个非常广泛的概念,小到一个属性的封装,大到一个框架或者一个项目的封装,下面这篇文章主要给大家介绍了关于java中封装的那点事,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-05-05
  • Spring Boot如何排除自动加载数据源

    Spring Boot如何排除自动加载数据源

    这篇文章主要介绍了Spring Boot如何排除自动加载数据源,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-12-12
  • 在Spring中利用@Order注解对bean和依赖进行排序

    在Spring中利用@Order注解对bean和依赖进行排序

    在Spring框架中,@Order是一个经常被忽视但非常重要的注解,在项目开发中,当我们需要维护bean的特定顺序或者存在许多相同类型的bean时,这个注解就发挥了作用,这篇文章讲的就是如何利用@Order注解对bean和依赖进行排序,需要的朋友可以参考下
    2023-11-11

最新评论