本文分享自華為雲社群《springboot動态切換資料源-雲社群-華為雲》,作者:小陳沒煩惱 。
前言
在公司的系統裡,由于資料量較大,是以配置了多個資料源,它會根據使用者所在的地區去覺得查詢那個一個資料庫。這樣就産生了動态切換資料源的場景。
今天,就模拟一下在主庫查詢訂單資訊查詢不到的時候,切換資料源去曆史庫裡面查詢。
實作效果
首先我們設定查詢的資料庫為db1,可以看到通過訂單号沒有查到訂單資訊,然後我們重置資料源,重新設定為db2,同樣的訂單号就可以查詢到資訊。
資料庫準備
建立兩個資料庫db1和db2,db1作為主庫,db2作為曆史庫
兩個庫中都有一個訂單表biz_order,主庫中沒有資料,曆史庫中有我們要查詢的資料。
代碼編寫
1.建立一個springboot項目,引入所需依賴
<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配置資料庫資訊
這裡我們配置兩個資料庫的資訊
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.建立資料源對象,并注入spring容器中
建立DynamicDataSourceConfig.java檔案,在該配置檔案中讀取yaml配置的資料源資訊,并且通過該資訊構造資料源對象,然後通過@Bean注解注入到spring容器中。
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.資料源配置上下文資訊
建立DynamicDataSourceHolder.java檔案,該檔案通過ThreadLocal,實作為每一個線程建立一個儲存資料源配置的上下文。并且提供setDataSource和getDataSource靜态方法來設定和擷取資料源的名稱。
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.重寫資料源配置類
建立DynamicDataSource.java檔案,該類繼承AbstractRoutingDataSource 類,重寫父類determineCurrentLookupKey和afterPropertiesSet方法。
這裡我們重寫父類中afterPropertiesSet方法(為什麼要重寫在這個方法,可以看文章最後對于druid的源碼的講解),在這個方法裡我們将spring容器中的所有的資料源,都給放到map裡,然後後續我們根據map中的key來擷取不同的資料源,super.afterPropertiesSet();通過這個方法設定上資料源
在類上加上@Primary注解,讓spring容器優先使用我們自定義的資料源,否則還是會使用預設的資料源配置。
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();
}
}
druid資料源配置解讀
點開我們剛剛繼承的AbstractRoutingDataSource抽象類,可以看到它又繼承了AbstractDataSource 實作了InitializingBean接口。
然後我們在看一下druid的資料源配置是怎麼實作的,點開DruidDataSourceWrapper類,可以看到它也是繼承了AbstractDataSource 實作了InitializingBean接口。并且,讀取的是yaml檔案中spring.datasource.druid下面配置的資料庫連接配接資訊。
而我們自定的一的資料源讀取的是spring.datasource.db1下面配置的資料庫連接配接資訊。
druid的資料源配置,實作了接口中afterPropertiesSet,在這個方法中設定了資料庫的基本資訊,例如,資料庫連接配接位址、使用者名、密碼以及資料庫連接配接驅動資訊。
關注#華為雲開發者聯盟# 點選下方,第一時間了解華為雲新鮮技術~
華為雲部落格_大資料部落格_AI部落格_雲計算部落格_開發者中心-華為雲