spring动态切换多数据源

一、数据库连接信息配置

Spring Boot 的默认配置文件是 application.properties ,由于有两个数据库配置,因此添加配置文件 jbdc.properties ,添加以下自定义的主从数据库配置:

# master
spring.datasource.master.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.master.jdbc-url=jdbc:mysql://localhost:3306/master?useSSL=false&serverTimezone=GMT%2B8&characterEncoding=UTF-8
spring.datasource.master.username=root
spring.datasource.master.password=111111

# slave
spring.datasource.slave.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.slave.jdbc-url=jdbc:mysql://localhost:3306/slave?useSSL=false&serverTimezone=GMT%2B8&characterEncoding=UTF-8
spring.datasource.slave.username=root
spring.datasource.slave.password=111111

二、数据源配置

根据连接信息,把数据源注入到Spring中,添加DataSourceSupper文件,配置如下:

@Configuration
@PropertySource("classpath:config/jdbc.properties")
@Slf4j
public class DataSourceSupper implements ApplicationContextAware {

    @Bean(initMethod = "init")
    @ConfigurationProperties("spring.datasource.master")
    public DruidDataSource dataSourceMaster() {
        return DruidDataSourceBuilder.create().build();
    }

    @Bean(initMethod = "init")
    @ConfigurationProperties("spring.datasource.slave")
    public DruidDataSource dataSourceSlave() {
        return DruidDataSourceBuilder.create().build();
    }
    
    @Autowired
    ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @Primary
    @DependsOn()
    @Bean
    public AbstractRoutingDataSource dataSource() {
        Map<String, DruidDataSource> beans = applicationContext.getBeansOfType(DruidDataSource.class);
        DynamicDataSource proxy = new DynamicDataSource();
        //设置默认数据源
        proxy.setDefaultTargetDataSource(dataSourceMaster());
        Map<Object, Object> targetDataSources = new HashMap<>(beans.size());
        //查找全部数据源
        List<String> source = new ArrayList<>();
        for (Map.Entry<String, DruidDataSource> d : beans.entrySet()) {
            String value = StringUtils.subString(d.getKey(), "dataSource", null).toLowerCase();
            source.add(value);
            targetDataSources.put(value, d.getValue());
        }
        proxy.setTargetDataSources(targetDataSources);
        //刷新数据源
        proxy.afterPropertiesSet();
        log.info("数据源配置完毕,数据源个数:" + targetDataSources.size() + "(" + String.join(",", source) + ")");
        return proxy;
    }

注意:
此处使用 PropertySource 指定数据库配置文件,ConfigurationProperties 指定数据源配置前缀
实现ApplicationContextAware接口,可以拿到applicationContext,通过getBean取出所有数据源之后配置默认数据源。
使用 Map 保存多个数据源,并设置到动态数据源对象中,其中key我们做了处理,存入的是master和slave。
使用注解 Primary 优先从动态数据源中获取。

三、动态数据源设置

前面的配置已把多个数据源注入到 Spring 中,接着对动态数据源进行配置。
1、添加jdbc依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>

2、添加动态数据源类

public class DynamicDataSource extends AbstractRoutingDataSource {

    @Override
    protected Object determineCurrentLookupKey() {
        return DatabaseContextHolder.getDataSource();
    }
}

注意:
继承抽象类 AbstractRoutingDataSource ,需要实现方法 determineCurrentLookupKey,即路由策略。
DatabaseContextHolder类是我们自定义的Threadlocal,可以通过改变Threadlocal从而动态切换数据源。

3、DatabaseContextHolder类配置

public class DatabaseContextHolder {
    /**
     * 使用ThreadLocal把数据源与当前线程绑定
     */
    private static final ThreadLocal<String> dataSources = new ThreadLocal<>();

    public static void setDataSource(String dataSourceName) {
        dataSources.set(dataSourceName);
    }

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

    public static void clearDataSource() {
        dataSources.remove();
    }
}

四、动态数据源使用

DatabaseContextHolder.setDataSource("slave");
业务代码
业务代码
业务代码
DatabaseContextHolder.clearDataSource();

默认是使用master数据源查询,可通过setDataSource切换slave数据源,使用后要记得clearDataSource,防止Threadlocal内存泄露。

五、参考资料

1、https://juejin.cn/post/6844904050262016007

上一篇:k8s之xtrackup备份MySQL主从节点测试分析


下一篇:Redis主从复制