Java高频面试题:MyBatis如何实现动态数据源切换?

大家好,我是锋哥。今天分享关于【Java高频面试题:MyBatis如何实现动态数据源切换?】面试题 。希望对大家有帮助;

Java高频面试题:MyBatis如何实现动态数据源切换?

1️⃣ 基本思路

在 MyBatis(或 Spring + MyBatis)中,动态切换数据源的核心思想是:

  1. 定义多个数据源(DataSource)。
  2. 使用一个 动态数据源路由器(DynamicDataSource),在运行时根据上下文决定使用哪个数据源。
  3. 在方法或线程执行前,设置当前线程使用的数据源标识(通常用 ThreadLocal 保存)。
  4. MyBatis 的 SqlSession 或 Spring JdbcTemplate 会通过动态数据源路由器获取当前的数据源。

关键点:线程安全。每个线程单独维护自己的数据源标识,防止并发下数据源错乱。


2️⃣ 核心实现步骤

2.1 定义数据源

通常在 application.ymlapplication.properties 中配置多个数据源,例如:

复制代码
spring:
  datasource:
    master:
      url: jdbc:mysql://localhost:3306/master
      username: root
      password: root
      driver-class-name: com.mysql.cj.jdbc.Driver
    slave:
      url: jdbc:mysql://localhost:3306/slave
      username: root
      password: root
      driver-class-name: com.mysql.cj.jdbc.Driver

在 Spring 中配置对应的 DataSource Bean:

复制代码
@Bean
@Primary
@ConfigurationProperties(prefix = "spring.datasource.master")
public DataSource masterDataSource() {
    return DataSourceBuilder.create().build();
}

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

2.2 创建动态数据源

复制代码
public class DynamicDataSource extends AbstractRoutingDataSource {

    // 核心方法,根据当前线程的数据源标识返回对应 DataSource
    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceContextHolder.getDataSource();
    }
}

DataSourceContextHolder 用于管理线程数据源:

复制代码
public class DataSourceContextHolder {
    private static final ThreadLocal<String> CONTEXT = new ThreadLocal<>();

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

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

    public static void clear() {
        CONTEXT.remove();
    }
}

然后在配置类里:

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

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

2.3 MyBatis 配置动态数据源

在 Spring Boot 中,通常直接配置 MyBatis 使用动态数据源:

复制代码
@Bean
public SqlSessionFactory sqlSessionFactory(DataSource dynamicDataSource) throws Exception {
    SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
    sessionFactory.setDataSource(dynamicDataSource);
    return sessionFactory.getObject();
}

2.4 在代码中切换数据源

可以在服务方法上通过 AOP 或注解 来切换数据源,例如:

复制代码
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface DataSource {
    String value() default "master";
}

AOP 切面实现:

复制代码
@Aspect
@Component
public class DataSourceAspect {

    @Before("@annotation(dataSource)")
    public void switchDataSource(DataSource dataSource) {
        DataSourceContextHolder.setDataSource(dataSource.value());
    }

    @After("@annotation(dataSource)")
    public void clearDataSource(DataSource dataSource) {
        DataSourceContextHolder.clear();
    }
}

然后在 Service 方法上标注:

复制代码
@Service
public class UserService {

    @DataSource("slave")
    public List<User> getUsersFromSlave() {
        return userMapper.selectAll();
    }

    @DataSource("master")
    public void insertUser(User user) {
        userMapper.insert(user);
    }
}

✅ 这样就实现了 按方法动态切换数据源


3️⃣ 注意事项

  1. 事务管理

    动态数据源切换必须配合 Spring 的事务管理,否则可能出现事务跨数据源异常。

    • 通常使用 @Transactional 注解配合 AbstractRoutingDataSource 即可。
  2. 线程安全

    必须使用 ThreadLocal 来保证每个线程独立使用数据源标识,否则并发请求可能混乱。

  3. 性能考虑

    切换数据源比单一数据源略有开销,尤其是在高并发下,需要注意连接池的配置。


💡 总结:

MyBatis 的动态数据源切换本质是 AbstractRoutingDataSource + ThreadLocal 的组合,通过 AOP 或手动设置当前线程的数据源来实现方法级的数据源切换。

相关推荐
程序喵大人7 小时前
【C++进阶】STL容器与迭代器 - 01 STL 容器先解决元素放在哪里
开发语言·c++·stl
cui_ruicheng8 小时前
Python从入门到实战(十六):多进程编程
开发语言·python
Jelena157795857928 小时前
电商运营分析数据比价接口实战:多平台价格监控与智能决策系统
java·大数据·数据库
wdfk_prog9 小时前
嵌入式面试真题第 15 题:不可恢复异常后的通用崩溃快照、调用栈保存与离线分析架构
linux·开发语言·面试·架构
晴空了无痕9 小时前
从 Go 基础到 K8s:一条可落地的 Go 服务端成长路线
开发语言·后端·golang·kubernetes
神明不懂浪漫9 小时前
【第五章】Java中的继承与多态
java·开发语言
神州世通10 小时前
解密企业通信安全防线:Avaya Aura 三层安全架构深度解析
开发语言·php
AC赳赳老秦11 小时前
企业工商公开信息采集分析:OpenClaw 批量查询企业工商信息,生成企业画像报告
大数据·开发语言·python·自动化·php·deepseek·openclaw
AI多Agent协作实战派11 小时前
AI多Agent协作系统实战(十七):凌晨4点,我的AI系统在“假装工作“——3个bug同时爆炸的5小时
java·前端·bug
gaolei_eit11 小时前
Java+Ai+vue
java·spring·maven