Spring框架下利用AbstractRoutingDataSource配置多数据源

2022-06-29 08:09:25

首先看Druid数据库多数据源Demo

Spring的多数据源支持---AbstractRoutingDataSource,AbstractRoutingDataSource定义了抽象的determineCurrentLookupKey方法,子类实现此方法,来确定要使用的数据源

public abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean {
	 protected DataSource determineTargetDataSource() {
		Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");
		Object lookupKey = determineCurrentLookupKey();
		DataSource dataSource = this.resolvedDataSources.get(lookupKey);
		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;
	}

        // 确定当前要使用的数据源
        protected abstract Object determineCurrentLookupKey();
}

Druid 实现多数据源支持,核心是Overwrite AbstractRoutingDataSource 的 determineCurrentLookupKey 方法

  public class MultipleDataSource extends AbstractRoutingDataSource {
	public static final String DATA_SOURCE_A = "dataSourceTestA";  // A库
	public static final String DATA_SOURCE_B = "dataSourceTestB";  // B库

	private static final ThreadLocal<String> dataSourceKey = new ThreadLocal<String>();

	public static void setDataSourceKey(String dataSource) {
		dataSourceKey.set(dataSource);
	}

	@Override
	protected Object determineCurrentLookupKey() {
		return dataSourceKey.get();
	}

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

AbstractRoutingDataSourceApplication实战:

DataSourceLeonProperties

package center.leon.oak.druid.abstract_routing_data_source.properties;

import lombok.Data;
import lombok.experimental.Accessors;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import java.util.List;

/**
 * @program: leon
 * @description:
 * @author: Leon
 * @create: 2021-07-22 13:56
 **/
@Data
@Component
@ConfigurationProperties(value = "spring.datasource.leon")
public class DataSourceLeonProperties {

    /**
     * 默认数据源
     */
    private DataSourceLeon defaultDataSourceLeon;
    /**
     * 数据源列表
     */
    private List<DataSourceLeon> dataSourceLeonList;


    /**
     * 自定义数据源
     */
    @Data
    @Accessors(chain = true)
    public static class DataSourceLeon {
        /**
         * 数据源自定义名称
         */
        private String dataSourceName;
        /**
         * 数据源驱动
         */
        private String driverClassName;
        /**
         * 数据源URL
         */
        private String url;
        /**
         *
         */
        private String username;
        /**
         *
         */
        private String password;
    }
}

MultipleDataSource

package center.leon.oak.druid.abstract_routing_data_source.config;

import center.leon.oak.druid.abstract_routing_data_source.properties.DataSourceLeonProperties;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;

/**
 * @program: leon
 * @description:
 * @author: Leon
 * @create: 2021-07-22 11:31
 **/
@Component
public class MultipleDataSource extends AbstractRoutingDataSource implements InitializingBean {

    ThreadLocal<String> data_source_name = new ThreadLocal<>();

    @Autowired
    private DataSourceLeonProperties datasourceLeonProperties;

    @Override
    public void afterPropertiesSet() {
        init();
    }

    private void init() {
        // 设置默认数据源
        if (null != datasourceLeonProperties.getDefaultDataSourceLeon()) {
            DataSourceLeonProperties.DataSourceLeon dataSourceLeon = datasourceLeonProperties.getDefaultDataSourceLeon();
            DruidDataSource druidDataSource = DruidDataSourceBuilder.create().build();
            druidDataSource.setDriverClassName(dataSourceLeon.getDriverClassName());
            druidDataSource.setUrl(dataSourceLeon.getUrl());
            druidDataSource.setUsername(dataSourceLeon.getUsername());
            druidDataSource.setPassword(dataSourceLeon.getPassword());
            super.setDefaultTargetDataSource(druidDataSource);
        }

        // 设置多数据源
        Map<Object, Object> targetDataSources = new HashMap<>();
        for (DataSourceLeonProperties.DataSourceLeon dataSourceLeon : datasourceLeonProperties.getDataSourceLeonList()) {
            DruidDataSource druidDataSource = DruidDataSourceBuilder.create().build();
            druidDataSource.setDriverClassName(dataSourceLeon.getDriverClassName());
            druidDataSource.setUrl(dataSourceLeon.getUrl());
            druidDataSource.setUsername(dataSourceLeon.getUsername());
            druidDataSource.setPassword(dataSourceLeon.getPassword());

            targetDataSources.put(dataSourceLeon.getDataSourceName(), druidDataSource);
        }
        super.setTargetDataSources(targetDataSources);

        // 调用父类 afterPropertiesSet 方法
        // 将 targetDataSources 数据 填充到 resolvedDataSources 中
        // determineCurrentLookupKey 返回的 lookupKey 本质上是在 resolvedDataSources 中查找 dataSource 返回
        // 将 defaultTargetDataSource 数据 填充到 resolvedDefaultDataSource 中
        // resolvedDataSources 中 查找不到合适的 dataSource 时返回。
        super.afterPropertiesSet();
    }

    @Override
    protected Object determineCurrentLookupKey() {
        return data_source_name.get();
    }

    public void setDb_tb_name(String dataSourceName) {
        data_source_name.set(dataSourceName);
    }

    public void clearDb_tb_name() {
        data_source_name.remove();
    }
}

MonkeyDO

package center.leon.oak.druid.abstract_routing_data_source.dto;

import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.experimental.Accessors;

/**
 * @program: leon
 * @description:
 * @author: Leon
 * @create: 2021-07-22 12:05
 **/
@Data
@TableName(value = "monkey")
@Accessors(chain = true)
public class MonkeyDO {
    private Long id;
    private String name;
    private Long version;
}

MonkeyMapper

package center.leon.oak.druid.abstract_routing_data_source.mapper;

import center.leon.oak.druid.abstract_routing_data_source.dto.MonkeyDO;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;

/**
 * @program: leon
 * @description:
 * @author: Leon
 * @create: 2021-07-22 12:04
 **/
@Mapper
public interface MonkeyMapper extends BaseMapper<MonkeyDO> {
}

MonkeyService

package center.leon.oak.druid.abstract_routing_data_source.service;

import center.leon.oak.druid.abstract_routing_data_source.dto.MonkeyDO;

/**
 * @program: leon
 * @description:
 * @author: Leon
 * @create: 2021-07-22 12:00
 **/
public interface MonkeyService {

    MonkeyDO get(Long id);

    MonkeyDO getInDataSource(Long id, String dataSourceName);
}

MonkeyServiceImpl

package center.leon.oak.druid.abstract_routing_data_source.service.impl;

import center.leon.oak.druid.abstract_routing_data_source.config.MultipleDataSource;
import center.leon.oak.druid.abstract_routing_data_source.dto.MonkeyDO;
import center.leon.oak.druid.abstract_routing_data_source.mapper.MonkeyMapper;
import center.leon.oak.druid.abstract_routing_data_source.service.MonkeyService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * @program: leon
 * @description:
 * @author: Leon
 * @create: 2021-07-22 12:03
 **/
@Service
public class MonkeyServiceImpl implements MonkeyService {

    @Autowired
    private MonkeyMapper monkeyMapper;
    @Autowired
    private MultipleDataSource multipleDataSource;

    @Override
    public MonkeyDO get(Long id) {
        return monkeyMapper.selectById(id);
    }

    @Override
    public MonkeyDO getInDataSource(Long id, String dataSourceName) {
        try {
            multipleDataSource.setDb_tb_name(dataSourceName);
            return monkeyMapper.selectById(id);
        } finally {
            multipleDataSource.clearDb_tb_name();
        }
    }
}

PrintMonkey

package center.leon.oak.druid.abstract_routing_data_source.task;

import center.leon.oak.druid.abstract_routing_data_source.dto.MonkeyDO;
import center.leon.oak.druid.abstract_routing_data_source.service.MonkeyService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

/**
 * @program: leon
 * @description:
 * @author: Leon
 * @create: 2021-07-22 21:28
 **/
@Slf4j
@Component
public class PrintMonkey {

    @Autowired
    private MonkeyService monkeyService;

    @Scheduled(fixedRate = 6 * 1000)
    public void printMonkey_localhost() {
        MonkeyDO monkeyDO = monkeyService.getInDataSource(1L, "localhost");
        log.info("threadName : {}, monkey : {}", Thread.currentThread().getName(), monkeyDO);
    }

    @Scheduled(fixedRate = 6 * 1000)
    public void printMonkey_www_leon_center() {
        MonkeyDO monkeyDO = monkeyService.getInDataSource(1L, "www_leon_center");
        log.info("threadName : {}, monkey : {}", Thread.currentThread().getName(), monkeyDO);
    }
}

application-druid.properties

spring.datasource.type=center.leon.oak.druid.abstract_routing_data_source.config.MultipleDataSource

spring.datasource.leon.data-source-leon-list[0].data-source-name=localhost
spring.datasource.leon.data-source-leon-list[0].driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.leon.data-source-leon-list[0].url=jdbc:mysql://localhost:3306/zoo?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8&useSSL=false
spring.datasource.leon.data-source-leon-list[0].username=root
spring.datasource.leon.data-source-leon-list[0].password=Liang123,

spring.datasource.leon.data-source-leon-list[1].data-source-name=www_leon_center
spring.datasource.leon.data-source-leon-list[1].driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.leon.data-source-leon-list[1].url=jdbc:mysql://www.leon.center:3306/zoo?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8&useSSL=false
spring.datasource.leon.data-source-leon-list[1].username=lmy
spring.datasource.leon.data-source-leon-list[1].password=Lmy123456,


spring.task.scheduling.pool.size=2

AbstractRoutingDataSourceApplication

package center.leon.oak.druid.abstract_routing_data_source;

import center.leon.oak.druid.abstract_routing_data_source.config.MultipleDataSource;
import center.leon.oak.druid.abstract_routing_data_source.dto.MonkeyDO;
import center.leon.oak.druid.abstract_routing_data_source.properties.DataSourceLeonProperties;
import center.leon.oak.druid.abstract_routing_data_source.service.MonkeyService;
import center.leon.oak.druid.abstract_routing_data_source.service.impl.MonkeyServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.scheduling.annotation.EnableScheduling;

/**
 * @program: leon
 * @description:
 * @author: Leon
 * @create: 2021-07-22 11:28
 **/
@Slf4j
@EnableScheduling
@EnableConfigurationProperties
@SpringBootApplication
public class AbstractRoutingDataSourceApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext run = SpringApplication.run(AbstractRoutingDataSourceApplication.class, args);
        DataSourceLeonProperties dataSourceLeonProperties = run.getBean(DataSourceLeonProperties.class);
        log.info("dataSourceLeonProperties : {}", dataSourceLeonProperties);
        MonkeyService monkeyService = run.getBean(MonkeyServiceImpl.class);

        final MultipleDataSource multipleDataSource = run.getBean(MultipleDataSource.class);

        multipleDataSource.setDb_tb_name("localhost");
        MonkeyDO monkey_1 = monkeyService.get(1L);
        log.info("monkey_1 : {}", monkey_1);
        multipleDataSource.clearDb_tb_name();

        multipleDataSource.setDb_tb_name("www_leon_center");
        MonkeyDO monkey_2 = monkeyService.get(1L);
        log.info("monkey_2 : {}", monkey_2);
        multipleDataSource.clearDb_tb_name();
    }
}

Console

2021-07-22 22:11:39.813  INFO 69282 --- [           main] d.a.AbstractRoutingDataSourceApplication : show Time ...
2021-07-22 22:11:39.813  INFO 69282 --- [           main] d.a.AbstractRoutingDataSourceApplication : dataSourceLeonProperties : DataSourceLeonProperties(defaultDataSourceLeon=null, dataSourceLeonList=[DataSourceLeonProperties.DataSourceLeon(dataSourceName=localhost, driverClassName=com.mysql.cj.jdbc.Driver, url=jdbc:mysql://localhost:3306/zoo?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8&useSSL=false, username=root, password=Liang123,), DataSourceLeonProperties.DataSourceLeon(dataSourceName=www_leon_center, driverClassName=com.mysql.cj.jdbc.Driver, url=jdbc:mysql://www.leon.center:3306/zoo?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8&useSSL=false, username=lmy, password=Lmy123456,)])
2021-07-22 22:11:39.864  INFO 69282 --- [   scheduling-2] com.alibaba.druid.pool.DruidDataSource   : {dataSource-2} inited
2021-07-22 22:11:39.864  INFO 69282 --- [           main] com.alibaba.druid.pool.DruidDataSource   : {dataSource-1} inited
2021-07-22 22:11:40.026  INFO 69282 --- [           main] d.a.AbstractRoutingDataSourceApplication : monkey_1 : MonkeyDO(id=1, name=write, version=2)
2021-07-22 22:11:40.026  INFO 69282 --- [   scheduling-1] c.l.o.d.a.task.PrintMonkey               : threadName : scheduling-1, monkey : MonkeyDO(id=1, name=write, version=2)
2021-07-22 22:11:40.067  INFO 69282 --- [   scheduling-2] c.l.o.d.a.task.PrintMonkey               : threadName : scheduling-2, monkey : MonkeyDO(id=1, name=monkey sun, version=500)
2021-07-22 22:11:40.075  INFO 69282 --- [           main] d.a.AbstractRoutingDataSourceApplication : monkey_2 : MonkeyDO(id=1, name=monkey sun, version=500)
2021-07-22 22:11:40.367  INFO 69282 --- [on(3)-127.0.0.1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring DispatcherServlet 'dispatcherServlet'
2021-07-22 22:11:40.367  INFO 69282 --- [on(3)-127.0.0.1] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
2021-07-22 22:11:40.367 TRACE 69282 --- [on(3)-127.0.0.1] o.s.web.servlet.DispatcherServlet        : Detected org.springframework.web.multipart.support.StandardServletMultipartResolver@42d0e41
2021-07-22 22:11:40.367 TRACE 69282 --- [on(3)-127.0.0.1] o.s.web.servlet.DispatcherServlet        : Detected org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver@48f2054d
2021-07-22 22:11:40.367 TRACE 69282 --- [on(3)-127.0.0.1] o.s.web.servlet.DispatcherServlet        : Detected org.springframework.web.servlet.theme.FixedThemeResolver@6d6cd1e0
2021-07-22 22:11:40.368 TRACE 69282 --- [on(3)-127.0.0.1] o.s.web.servlet.DispatcherServlet        : Detected DefaultRequestToViewNameTranslator
2021-07-22 22:11:40.369 TRACE 69282 --- [on(3)-127.0.0.1] o.s.web.servlet.DispatcherServlet        : Detected SessionFlashMapManager
2021-07-22 22:11:40.369 DEBUG 69282 --- [on(3)-127.0.0.1] o.s.web.servlet.DispatcherServlet        : enableLoggingRequestDetails='false': request parameters and headers will be masked to prevent unsafe logging of potentially sensitive data
2021-07-22 22:11:40.369  INFO 69282 --- [on(3)-127.0.0.1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 2 ms
2021-07-22 22:11:45.812  INFO 69282 --- [   scheduling-1] c.l.o.d.a.task.PrintMonkey               : threadName : scheduling-1, monkey : MonkeyDO(id=1, name=write, version=2)
2021-07-22 22:11:45.824  INFO 69282 --- [   scheduling-2] c.l.o.d.a.task.PrintMonkey               : threadName : scheduling-2, monkey : MonkeyDO(id=1, name=monkey sun, version=500)
2021-07-22 22:11:51.808  INFO 69282 --- [   scheduling-1] c.l.o.d.a.task.PrintMonkey               : threadName : scheduling-1, monkey : MonkeyDO(id=1, name=write, version=2)
2021-07-22 22:11:51.822  INFO 69282 --- [   scheduling-2] c.l.o.d.a.task.PrintMonkey               : threadName : scheduling-2, monkey : MonkeyDO(id=1, name=monkey sun, version=500)
2021-07-22 22:11:57.807  INFO 69282 --- [   scheduling-1] c.l.o.d.a.task.PrintMonkey               : threadName : scheduling-1, monkey : MonkeyDO(id=1, name=write, version=2)
2021-07-22 22:11:57.818  INFO 69282 --- [   scheduling-2] c.l.o.d.a.task.PrintMonkey               : threadName : scheduling-2, monkey : MonkeyDO(id=1, name=monkey sun, version=500)
2021-07-22 22:12:03.809  INFO 69282 --- [   scheduling-1] c.l.o.d.a.task.PrintMonkey               : threadName : scheduling-1, monkey : MonkeyDO(id=1, name=write, version=2)
2021-07-22 22:12:04.051  INFO 69282 --- [   scheduling-2] c.l.o.d.a.task.PrintMonkey               : threadName : scheduling-2, monkey : MonkeyDO(id=1, name=monkey sun, version=500)
2021-07-22 22:12:09.811  INFO 69282 --- [   scheduling-1] c.l.o.d.a.task.PrintMonkey               : threadName : scheduling-1, monkey : MonkeyDO(id=1, name=write, version=2)
2021-07-22 22:12:09.824  INFO 69282 --- [   scheduling-2] c.l.o.d.a.task.PrintMonkey               : threadName : scheduling-2, monkey : MonkeyDO(id=1, name=monkey sun, version=500)
2021-07-22 22:12:15.810  INFO 69282 --- [   scheduling-1] c.l.o.d.a.task.PrintMonkey               : threadName : scheduling-1, monkey : MonkeyDO(id=1, name=write, version=2)
2021-07-22 22:12:15.821  INFO 69282 --- [   scheduling-2] c.l.o.d.a.task.PrintMonkey               : threadName : scheduling-2, monkey : MonkeyDO(id=1, name=monkey sun, version=500)
  • 作者:limeOracle
  • 原文链接:https://blog.csdn.net/limeOracle/article/details/119009807
    更新时间:2022-06-29 08:09:25