# 1、创建配置类

@Configuration
public class DataSourceConfig {
    @Bean
    @ConfigurationProperties("spring.datasource.master")
    public DataSource masterDataSource() {
        return DruidDataSourceBuilder.create().build();
    }

    @Bean
    @ConfigurationProperties("spring.datasource.slave01")
    @ConditionalOnProperty(prefix = "spring.datasource.slave01", name = "enabled", havingValue = "true")
    public DataSource slave01DataSource() {
        return DruidDataSourceBuilder.create().build();
    }

    @Bean
    @Primary
    public DynamicDataSource dataSource() {
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        //配置默认数据源
        dynamicDataSource.setDefaultTargetDataSource(masterDataSource());
        //配置多数据源
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put(DataSourceType.MASTER.getSourceName(), masterDataSource());
        targetDataSources.put(DataSourceType.SLAVE01.getSourceName(), slave01DataSource());
        dynamicDataSource.setTargetDataSources(targetDataSources);
        return dynamicDataSource;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

# 2、配置yaml文件

spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    # 主库数据源
    master:
      driver-class-name: com.mysql.jdbc.Driver
      url: jdbc:mysql://192.168.100.60:3306/demo?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
      username: root
      password: root
    # 从库数据源
    slave01:
      enabled: true
      driver-class-name: com.mysql.jdbc.Driver
      url: jdbc:mysql://192.168.100.60:3306/demo01?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
      username: root
      password: root
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 3、配置数据源切换

public class DynamicDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceContextHolder.getDataSourceType();
    }
}
1
2
3
4
5
6

# 4、使用线程类切换数据源

@Slf4j
public class DataSourceContextHolder {
    /**
     * 使用ThreadLocal维护变量,ThreadLocal为每个使用该变量的线程提供独立的变量副本,
     *  所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
     */
    private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();

    /**
     * 设置数据源变量
     * @param dataSourceType
     */
    public static void setDataSourceType(String dataSourceType){
        log.info("切换到{}数据源", dataSourceType);
        CONTEXT_HOLDER.set(dataSourceType);
    }

    /**
     * 获取数据源变量
     * @return
     */
    public static String getDataSourceType(){
        return CONTEXT_HOLDER.get();
    }

    /**
     * 清空数据源变量
     */
    public static void clearDataSourceType(){
        CONTEXT_HOLDER.remove();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

# 5、多数据源的使用

public List<User> findAll() {
    DataSourceContextHolder.setDataSourceType("slave01");
    List<User> users = userDao.findAll();
    DataSourceContextHolder.clearDataSourceType();
    return users;
}
1
2
3
4
5
6

# 6、DataSource源码

protected DataSource determineTargetDataSource() {
    Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");
    // 根据determineCurrentLookupKey函数返回map的key
    Object lookupKey = determineCurrentLookupKey();
    DataSource dataSource = this.resolvedDataSources.get(lookupKey);
    // 如果找的到key对应的value则使用其他数据源,要不然使用默认的
    if (dataSource == null && (this.lenientFallback || lookupKey == null)) {
        dataSource = this.resolvedDefaultDataSource;
    }
    if (dataSource == null) {
        throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");
    }
    return dataSource;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Last Updated: 4/9/2024