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