In development, dynamic data source configuration is used a lot, such as in the use of multiple data sources, or switching between multiple DB. Here is a dynamic data source configuration scheme, both the two DB take mysql as an example.

Multiple data source configuration introduced

The introduction of mybatis and mysql in SpringBoot here is not said, do not understand can see springBoot mysql and mybatis introduction.

The data source configuration is as follows:

datasource:
  master:
    type: com.alibaba.druid.pool.DruidDataSource
    jdbc-url: JDBC: mysql: / / 127.0.0.1:3306 / sbac_master? autoReconnect=true&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true
    username: root
    password: 1234
    driver-class-name: com.mysql.cj.jdbc.Driver
  log:
    type: com.alibaba.druid.pool.DruidDataSource
    jdbc-url: JDBC: mysql: / / 127.0.0.1:3306 / sbac_log? autoReconnect=true&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true
    username: root
    password: 1234
    driver-class-name: com.mysql.cj.jdbc.Driver
Copy the code

The configuration of MyBatis is introduced as follows:

mybatis:
  config-location: classpath:mybatis-config.xml
  mapper-locations: classpath:com/lazycece/sbac/mysql/data/dao/*/mapper/*.xml
Copy the code

In this case, the springBoot automatic configuration function is used to configure myBatis information, but the data source is manually specified. Set master as the default data source with the master and log data sources specified:

@Configuration
public class MultiDataSource {

    public static final String MASTER_DATA_SOURCE = "masterDataSource";
    public static final String LOG_DATA_SOURCE = "logDataSource";

    @Bean(name = MultiDataSource.MASTER_DATA_SOURCE)
    @ConfigurationProperties(prefix = "datasource.master")
    public DataSource masterDataSource(a) {
        return DataSourceBuilder.create().build();
    }

    @Bean(name = MultiDataSource.LOG_DATA_SOURCE)
    @ConfigurationProperties(prefix = "datasource.log")
    public DataSource logDataSource(a) {
        return DataSourceBuilder.create().build();
    }

    @Primary
    @Bean(name = "dynamicDataSource")
    public DynamicDataSource dataSource(a) {
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        dynamicDataSource.setDefaultTargetDataSource(masterDataSource());
        Map<Object, Object> dataSourceMap = new HashMap<>(4);
        dataSourceMap.put(MASTER_DATA_SOURCE, masterDataSource());
        dataSourceMap.put(LOG_DATA_SOURCE, logDataSource());
        dynamicDataSource.setTargetDataSources(dataSourceMap);
        returndynamicDataSource; }}Copy the code

Dynamic data source routing implementation

With the configuration information in place, it’s time to talk about how to switch between multiple data sources. We are by implementing AbstractRoutingDataSource class determineCurrentLookupKey method to realize the dynamic routing of the data source, set the ThreadLocal variable stores the data source key thread protection, ensure between threads are not affected.

package com.lazycece.sbac.mysql.multi.config;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

/ * * *@author lazycece
 */
public class DynamicDataSource extends AbstractRoutingDataSource {
    private static final Logger LOGGER = LoggerFactory.getLogger(DynamicDataSource.class);

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

    static void changeDataSource(String dataSourceKey) {
        DATA_SOURCE_KEY.set(dataSourceKey);
    }

    static void clearDataSource(a) {
        DATA_SOURCE_KEY.remove();
    }

    @Override
    protected Object determineCurrentLookupKey(a) {
        String key = DATA_SOURCE_KEY.get();
        LOGGER.info("current data-source is {}", key);
        returnkey; }}Copy the code

Then, it is to use THE METHOD of AOP to achieve dynamic switching of data sources, annotations and aspects are defined as follows:

@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface DataSource {

    String value(a);
}

@Component
@Aspect
public class DataSourceConfig {

    @Before("@annotation(dataSource)")
    public void beforeSwitchDataSource(DataSource dataSource) {
        DynamicDataSource.changeDataSource(dataSource.value());
    }

    @After("@annotation(DataSource)")
    public void afterSwitchDataSource(a) { DynamicDataSource.clearDataSource(); }}Copy the code

Dynamic data source switching use

Dynamic DataSource switching simply requires the business to use the @datasource annotation to indicate which DataSource to use, as shown below (only the key code is posted here) :

@Service
public class DynamicDataSourceServiceImpl implements DynamicDataSourceService {

    @Resource
    private UserDao userDao;
    @Resource
    private SystemLogDao systemLogDao;

    @Override
    @DataSource(value = MultiDataSource.MASTER_DATA_SOURCE)
    public void addUserInfo(User user) {
        userDao.insert(user);
    }

    @Override
    @DataSource(value = MultiDataSource.MASTER_DATA_SOURCE)
    public User getUserInfo(String username) {
        return userDao.findByUsername(username);
    }

    @Override
    @DataSource(value = MultiDataSource.LOG_DATA_SOURCE)
    public void addSystemLog(SystemLog systemLog) {
        systemLogDao.insert(systemLog);
    }

    @Override
    @DataSource(value = MultiDataSource.LOG_DATA_SOURCE)
    public List<SystemLog> getSystemLogInfo(Date beginTime, Date endTime) {
        returnsystemLogDao.findByCreateTime(beginTime, endTime); }}Copy the code

Example source code

Case source address: github.com/lazycece/sp…