Example to explain Spring boot dynamic switching data source

Abstract: This article simulates switching the data source to query in the history database when the order information cannot be found in the main database.

This article is shared from Huawei Cloud Community " springboot dynamically switches data sources ", author: Xiao Chen is not worried.

foreword

In the company's system, due to the large amount of data, multiple data sources are configured, and it will query the database according to the region where the user is located, thus creating a scenario of dynamically switching data sources.

Today, let’s simulate switching the data source and querying in the history database when the order information cannot be found in the main database.

achieve effect

First, we set the query database to db1. We can see that no order information can be found through the order number. Then we reset the data source and reset it to db2. The information can be queried with the same order number.

database preparation

Create two new databases db1 and db2, db1 as the main database, db2 as the historical database

There is an order table biz_order in both libraries, there is no data in the main library, and the data we want to query exists in the history library.

code writing

1. Create a new springboot project and introduce the required dependencies

 <dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter</artifactId>
 </dependency>
 <!--引入druid-替换默认数据库连接池-->
 <dependency>
 <groupId>com.alibaba</groupId>
 <artifactId>druid-spring-boot-starter</artifactId>
 <version>1.2.15</version>
 </dependency>
 <dependency>
 <groupId>org.mybatis.spring.boot</groupId>
 <artifactId>mybatis-spring-boot-starter</artifactId>
 <version>2.2.2</version>
 </dependency>
 <!--mysql驱动-->
 <dependency>
 <groupId>mysql</groupId>
 <artifactId>mysql-connector-java</artifactId>
 <version>8.0.30</version>
 </dependency>
 <dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-test</artifactId>
 <scope>test</scope>
 </dependency>

2. application.yaml configuration database information

Here we configure the information of the two databases

spring:
 datasource:
    db1:
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://localhost/db1?characterEncoding=utf8&characterSetResults=utf8&autoReconnect=true&failOverReadOnly=false
      username: root
      password: root
      type: com.alibaba.druid.pool.DruidDataSource
    db2:
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://localhost/db2?characterEncoding=utf8&characterSetResults=utf8&autoReconnect=true&failOverReadOnly=false
      username: root
      password: root
      type: com.alibaba.druid.pool.DruidDataSource
mybatis:
  mapper-locations: classpath:mapper/*.xml

3. Create a data source object and inject it into the spring container

Create a new DynamicDataSourceConfig.java file, read the data source information of the yaml configuration in the configuration file, and construct a data source object through the information, and then inject it into the spring container through the @Bean annotation.

package com.it1997.config;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import javax.sql.DataSource;
@Configuration
public class DynamicDataSourceConfig {
    @Bean("dataSource1")
    @ConfigurationProperties(prefix = "spring.datasource.db1")
 public DataSource oneDruidDataSource() {
 return DruidDataSourceBuilder.create().build();
 }
    @Bean("dataSource2")
    @ConfigurationProperties(prefix = "spring.datasource.db2")
 public DataSource twoDruidDataSource() {
 return DruidDataSourceBuilder.create().build();
 }
    @Bean
 public DataSourceTransactionManager dataSourceTransactionManager1(@Qualifier("dataSource1") DataSource dataSource1) {
 DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
 dataSourceTransactionManager.setDataSource(dataSource1);
 return dataSourceTransactionManager;
 }
    @Bean
 public DataSourceTransactionManager dataSourceTransactionManager2(@Qualifier("dataSource2") DataSource dataSource2) {
 DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
 dataSourceTransactionManager.setDataSource(dataSource2);
 return dataSourceTransactionManager;
 }
}

4. Data source configuration context information

Create a new DynamicDataSourceHolder.java file, which uses ThreadLocal to create a context for saving the data source configuration for each thread. And provide setDataSource and getDataSource static methods to set and get the name of the data source.

package com.it1997.config;
public class DynamicDataSourceHolder {
 private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
 public static void setDataSource(String dataSource) {
 contextHolder.set(dataSource);
 }
 public static String getDataSource() {
 return contextHolder.get();
 }
 public static void clearDataSource() {
 contextHolder.remove();
 }
}

5. Rewrite the data source configuration class

Create a new DynamicDataSource.java file, which inherits the AbstractRoutingDataSource class and rewrites the determineCurrentLookupKey and afterPropertiesSet methods of the parent class.

Here we rewrite the afterPropertiesSet method in the parent class (why we rewrite this method, you can read the explanation of the source code of druid at the end of the article), in this method we put all the data sources in the spring container into the map , and then we obtain different data sources according to the key in the map, super.afterPropertiesSet(); set the data source through this method.

Add the @Primary annotation to the class to let the spring container use our custom data source first, otherwise the default data source configuration will still be used.

package com.it1997.config;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
@Component
@Primary
public class DynamicDataSource extends AbstractRoutingDataSource {
    @Resource
 DataSource dataSource1;
    @Resource
 DataSource dataSource2;
    @Override
 protected Object determineCurrentLookupKey() {
 return DynamicDataSourceHolder.getDataSource();
 }
    @Override
 public void afterPropertiesSet() {
 // 初始化所有数据源
        Map<Object, Object> targetDataSource = new HashMap<>();
 targetDataSource.put("db1", dataSource1);
 targetDataSource.put("db2", dataSource2);
 super.setTargetDataSources(targetDataSource);
 super.setDefaultTargetDataSource(dataSource1);
 super.afterPropertiesSet();
 }
}

Interpretation of druid data source configuration

Click on the AbstractRoutingDataSource abstract class we just inherited, and you can see that it inherits AbstractDataSource and implements the InitializingBean interface.

Then let's take a look at how the data source configuration of druid is implemented. Click on the DruidDataSourceWrapper class, and you can see that it also inherits AbstractDataSource and implements the InitializingBean interface. And, what is read is the database connection information configured under spring.datasource.druid in the yaml file.

And our custom one data source reads the database connection information configured under spring.datasource.db1.

Druid's data source configuration implements afterPropertiesSet in the interface. In this method, the basic information of the database is set, such as the database connection address, user name, password, and database connection driver information.

 

Click to follow and learn about Huawei Cloud's fresh technologies for the first time~

{{o.name}}
{{m.name}}

Guess you like

Origin my.oschina.net/u/4526289/blog/9093988