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

Java高频面试题:MyBatis如何实现动态数据源切换?
1️⃣ 基本思路
在 MyBatis(或 Spring + MyBatis)中,动态切换数据源的核心思想是:
- 定义多个数据源(DataSource)。
- 使用一个 动态数据源路由器(DynamicDataSource),在运行时根据上下文决定使用哪个数据源。
- 在方法或线程执行前,设置当前线程使用的数据源标识(通常用
ThreadLocal保存)。 - MyBatis 的
SqlSession或 SpringJdbcTemplate会通过动态数据源路由器获取当前的数据源。
关键点:线程安全。每个线程单独维护自己的数据源标识,防止并发下数据源错乱。
2️⃣ 核心实现步骤
2.1 定义数据源
通常在 application.yml 或 application.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️⃣ 注意事项
-
事务管理
动态数据源切换必须配合 Spring 的事务管理,否则可能出现事务跨数据源异常。
- 通常使用
@Transactional注解配合AbstractRoutingDataSource即可。
- 通常使用
-
线程安全
必须使用
ThreadLocal来保证每个线程独立使用数据源标识,否则并发请求可能混乱。 -
性能考虑
切换数据源比单一数据源略有开销,尤其是在高并发下,需要注意连接池的配置。
💡 总结:
MyBatis 的动态数据源切换本质是 AbstractRoutingDataSource + ThreadLocal 的组合,通过 AOP 或手动设置当前线程的数据源来实现方法级的数据源切换。