java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > MyBatis动态数据源切换

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

作者:墨瑾轩

在微服务架构和分布式系统中,动态数据源是应对多环境切换、读写分离、多租户等复杂场景的核心工具,MyBatis 与 Spring 结合后,可以灵活实现数据源的动态切换,以下是实现 MyBatis 动态数据源的完整方案,需要的朋友可以参考下

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

1. 核心概念

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. 使用场景

读写分离

多环境切换

多租户隔离

4. 注意事项

线程安全

事务一致性

性能优化

异常处理

5. 总结

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

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

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

您可能感兴趣的文章:
阅读全文