AbstractRoutingDataSource, spring configuration multiple data source issues

AbstractRoutingDataSource, spring configuration multiple data source issues

First introduce pom.xml dependencies

 <!--Test-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <version>2.3.12.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
            <version>2.3.12.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.0.1</version>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.1.1</version>
        </dependency>
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper-spring-boot-starter</artifactId>
            <version>1.2.13</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.18</version>
        </dependency>

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.1.1</version>
        </dependency>

Add configuration in application.yml

spring:
  application:
    name: tms
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    ds1:
      driver-class-name: Driver
      url: ${url}
      username: ${username}
      password: ${password}
    ds2:
      driver-class-name: Driver
      url: ${url}
      username: ${username}
      password: ${password}

Added two database configurations

Add the configuration of these two databases and inject them into the bean

package com.dualdb;

import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;


@Configuration
public class MyBootDataSourceConfig {<!-- -->
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.ds1")
    public DataSource dataSource1() {<!-- -->
        // Get the configuration in spring.datasource through the configuration address and create a DruidDataSource
        return DruidDataSourceBuilder.create().build();
    }

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.ds2")
    public DataSource dataSource2() {<!-- -->
        // Get the configuration in spring.datasource through the configuration address and create a DruidDataSource
        return DruidDataSourceBuilder.create().build();
    }
}

Configure the master-slave relationship of these two data sources, and switch flag

package com.config.dualdb;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

@Component
@Primary // When there are multiple datasources, set this source as the main injected bean, primary
public class DynamicDatasource extends AbstractRoutingDataSource {<!-- -->

    // Data source identification, in order to ensure thread safety use threadLocal
    public static ThreadLocal<String> threadLocal = new ThreadLocal<>();

    @Autowired
    DataSource dataSource1;

    @Autowired
    DataSource dataSource2;

    /**
     * The function of this method is to return the current data source ID
     *
     * @return
     */
    @Override
    protected Object determineCurrentLookupKey() {<!-- -->
        return threadLocal. get();
    }

    /**
     * Before overriding the parent class method, assign values to the parent class's basic properties
     */
    @Override
    public void afterPropertiesSet() {<!-- -->
        // Assign values to the main parameters of AbstractRoutingDataSource

        // targetDataSources initializes all data sources
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put("ds1", dataSource1);
        targetDataSources.put("ds2", dataSource2);
        super.setTargetDataSources(targetDataSources);

        // defaultTargetDataSource sets the default data source
        super.setDefaultTargetDataSource(dataSource1);

        super. afterPropertiesSet();
    }
    // Clear the threadLocal variable
}

Automatically switch data sources using aop

package com.aop;

import com.config.dualdb.DynamicDatasource;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Component
@Aspect
@Slf4j
public class DynamicDatasourceAspect {<!-- -->

    @Pointcut("execution(public * com.mapper.test1.*.*(..))")
    public void pointCut1() {<!-- -->
    }


    @Pointcut("execution(public * com.mapper.test2.*.*(..))")
    public void pointCut2() {<!-- -->
    }

    @Before(value = "pointCut1()")
    public void before1(JoinPoint jp) {<!-- -->
        String name = "Resolve the token to obtain the data source corresponding to the user";
        // Obtain the relevant information of the request through reflection, analyze it, and obtain the corresponding identification
        // Set which data source to request according to the identifier corresponding to the specific business
        // Optimization point, dynamically switch configuration according to the cut point
        DynamicDatasource.threadLocal.set("ds1");
        log. info(name);
    }

    @After("pointCut1()")
    public void after1(JoinPoint joinPoint) {<!-- -->
        DynamicDatasource.threadLocal.remove();
    }

    @Before(value = "pointCut2()")
    public void before2(JoinPoint jp) {<!-- -->
        String name = "Resolve the token to obtain the data source corresponding to the user";
        // Obtain the relevant information of the request through reflection, analyze it, and obtain the corresponding identification
        // Set which data source to request according to the identifier corresponding to the specific business
        DynamicDatasource.threadLocal.set("ds2");
        log.info(name);
    }

    @After("pointCut2()")
    public void after2(JoinPoint joinPoint) {<!-- -->
        DynamicDatasource.threadLocal.remove();
    }
}

Simply write aop to automatically switch, automatically obtain the data source identifier by parsing the current execution package name, and then set the current data source. Note that you need to modify the package in the mapper directory to the corresponding data source name.

import com.config.dualdb.DynamicDatasource;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Component
@Aspect
@Slf4j
public class DynamicDatasourceAspect {<!-- -->

    @Pointcut("execution(public * com.mapper.*.*.*(..))")
    public void pointCut1() {<!-- -->
    }

    @Before(value = "pointCut1()")
    public void before1(JoinPoint jp) {<!-- -->
        String name = "Resolve the token to obtain the data source corresponding to the user";
        // Obtain the relevant information of the request through reflection, analyze it, and obtain the corresponding identification
        // Set which data source to request according to the identifier corresponding to the specific business
        final String[] split = jp.getSignature().getDeclaringTypeName().split("\.");
        String flag = split[split.length - 2];
        DynamicDatasource.threadLocal.set(flag);
        log.info(name);
    }

    @After("pointCut1()")
    public void after1(JoinPoint joinPoint) {<!-- -->
        DynamicDatasource.threadLocal.remove();
    }
}

refer to:
Configuration reference:
[spring configures multiple data sources] spring connects to multiple databases, and the same set of projects configures multiple databases
Declaration transaction reference:
mybatis(plus) multiple data sources