摘要:本文模擬一下在主庫查詢訂單信息查詢不到的時候,切換數據源去歷史庫裡面查詢。 本文分享自華為雲社區《springboot動態切換數據源》,作者:小陳沒煩惱 。 前言 在公司的系統里,由於數據量較大,所以配置了多個數據源,它會根據用戶所在的地區去查詢那一個資料庫,這樣就產生了動態切換數據源的場景。 ...
摘要:本文模擬一下在主庫查詢訂單信息查詢不到的時候,切換數據源去歷史庫裡面查詢。
本文分享自華為雲社區《springboot動態切換數據源》,作者:小陳沒煩惱 。
前言
在公司的系統里,由於數據量較大,所以配置了多個數據源,它會根據用戶所在的地區去查詢那一個資料庫,這樣就產生了動態切換數據源的場景。
今天,就模擬一下在主庫查詢訂單信息查詢不到的時候,切換數據源去歷史庫裡面查詢。
實現效果
首先我們設置查詢的資料庫為db1,可以看到通過訂單號沒有查到訂單信息,然後我們重置數據源,重新設置為db2,同樣的訂單號就可以查詢到信息。
![](https://pic4.zhimg.com/80/v2-f4491408e1743eaeca486cf5b8a64137_720w.webp)
資料庫準備
新建兩個資料庫db1和db2,db1作為主庫,db2作為歷史庫
兩個庫中都有一個訂單表biz_order,主庫中沒有數據,歷史庫中有我們要查詢的數據。
![](https://pic3.zhimg.com/80/v2-71e156e5ce0d25155f32a80a58c3ef52_720w.webp)
![](https://pic2.zhimg.com/80/v2-15ab973c72a8ab4fd6ff0ae9b53f53ad_720w.webp)
代碼編寫
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介面。
![](https://pic4.zhimg.com/80/v2-9ed5fc176d0b75d5816b2427f054d94f_720w.webp)
然後我們在看一下druid的數據源配置是怎麼實現的,點開DruidDataSourceWrapper類,可以看到它也是繼承了AbstractDataSource 實現了InitializingBean介面。並且,讀取的是yaml文件中spring.datasource.druid下麵配置的資料庫連接信息。
而我們自定的一的數據源讀取的是spring.datasource.db1下麵配置的資料庫連接信息。
![](https://pic1.zhimg.com/80/v2-d829d5c974be9daf781e1fba6bd31ed4_720w.webp)
druid的數據源配置,實現了介面中afterPropertiesSet,在這個方法中設置了資料庫的基本信息,例如,資料庫連接地址、用戶名、密碼以及資料庫連接驅動信息。