Springboot如何設(shè)置多數(shù)據(jù)源,隨時(shí)切換
需求
接到一個(gè)任務(wù),把一個(gè)數(shù)據(jù)庫(kù)里面的數(shù)據(jù)定時(shí)導(dǎo)入到另外的數(shù)據(jù)庫(kù)中
但是又不允許我們通過(guò)binlog+canal同步,所以考慮起一個(gè)微服務(wù)充當(dāng)同步腳本的作用
且配置多數(shù)據(jù)庫(kù),并且支持隨時(shí)切換
環(huán)境
- 1、mysql多個(gè)庫(kù)
- 2、mysql+postgresql
思路
spring框架本身支持多數(shù)據(jù)源,我們查看他的定義
Spring的多數(shù)據(jù)源支持—AbstractRoutingDataSource,AbstractRoutingDataSource定義了抽象的determineCurrentLookupKey方法,子類實(shí)現(xiàn)此方法,來(lái)確定要使用的數(shù)據(jù)源
看下下面它的源碼:
public abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean { protected DataSource determineTargetDataSource() { Assert.notNull(this.resolvedDataSources, "DataSource router not initialized"); Object lookupKey = determineCurrentLookupKey(); DataSource dataSource = this.resolvedDataSources.get(lookupKey); if (dataSource == null && (this.lenientFallback || lookupKey == null)) { dataSource = this.resolvedDefaultDataSource; } if (dataSource == null) { throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]"); } return dataSource; } // 確定當(dāng)前要使用的數(shù)據(jù)源 protected abstract Object determineCurrentLookupKey(); }
所以我們只要寫(xiě)一個(gè)自定義類去繼承上面這個(gè)AbstractRoutingDataSource類,并重寫(xiě)determineCurrentLookupKey 方法即可
操作
包如下:
一、多個(gè)庫(kù)都是mysql類型
pom依賴
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.6.7</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>db-demo</artifactId> <version>0.0.1-SNAPSHOT</version> <name>db-demo</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.2.2</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
1、配置文件application.yml
#端口 server.port: 7788 spring.application.name: bddemo # mysql spring.datasource: driver-class-name: com.mysql.cj.jdbc.Driver #數(shù)據(jù)庫(kù)1 db1: jdbc-url: jdbc:mysql://127.0.0.1:3306/db1?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&useSSL=false username: root password: 123456 #數(shù)據(jù)庫(kù)2 db2: jdbc-url: jdbc:mysql://127.0.0.1:3306/db2?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&useSSL=false username: root password: 123456 # mybatis mybatis: mapper-locations: classpath:mapper/*Mapper.xml type-aliases-package: ccom.example.demo.*.entity
2、配置類
1) DataSourceConfig 數(shù)據(jù)庫(kù)配置類:
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.jdbc.DataSourceBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.transaction.PlatformTransactionManager; import javax.sql.DataSource; import java.util.HashMap; import java.util.Map; /** * 數(shù)據(jù)庫(kù)配置 * @date 2022/5/19 */ @Configuration public class DataSourceConfig { /** * 數(shù)據(jù)源1 * spring.datasource.db1:application.properteis中對(duì)應(yīng)屬性的前綴 * @return */ @Bean(name = "db1") @ConfigurationProperties(prefix = "spring.datasource.db1") public DataSource dataSourceOne() { return DataSourceBuilder.create().build(); } /** * 數(shù)據(jù)源2 * spring.datasource.db2:application.properteis中對(duì)應(yīng)屬性的前綴 * @return */ @Bean(name = "db2") @ConfigurationProperties(prefix = "spring.datasource.db2") public DataSource dataSourceTwo() { return DataSourceBuilder.create().build(); } /** * 動(dòng)態(tài)數(shù)據(jù)源: 通過(guò)AOP在不同數(shù)據(jù)源之間動(dòng)態(tài)切換 * @return */ @Primary @Bean(name = "dynamicDataSource") public DataSource dynamicDataSource() { DynamicDataSource dynamicDataSource = new DynamicDataSource(); // 默認(rèn)數(shù)據(jù)源 dynamicDataSource.setDefaultTargetDataSource(dataSourceOne()); // 配置多數(shù)據(jù)源 Map<Object, Object> dsMap = new HashMap<>(); dsMap.put("db1", dataSourceOne()); dsMap.put("db2", dataSourceTwo()); dynamicDataSource.setTargetDataSources(dsMap); return dynamicDataSource; } /** * 配置多數(shù)據(jù)源后IOC中存在多個(gè)數(shù)據(jù)源了,事務(wù)管理器需要重新配置,不然器不知道選擇哪個(gè)數(shù)據(jù)源 * 事務(wù)管理器此時(shí)管理的數(shù)據(jù)源將是動(dòng)態(tài)數(shù)據(jù)源dynamicDataSource * 配置@Transactional注解 * @return */ @Bean public PlatformTransactionManager transactionManager() { return new DataSourceTransactionManager(dynamicDataSource()); } }
2) DynamicDataSource 動(dòng)態(tài)數(shù)據(jù)源類:
import com.example.demo.utils.DataSourceUtil; import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; /** * 動(dòng)態(tài)數(shù)據(jù)源類 * @date 2022/2/11 */ public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DataSourceUtil.getDB(); } }
3、切換工具類:DataSourceUtil
/** * 數(shù)據(jù)源切換工具 * @date 2022/5/19 */ public class DataSourceUtil { /** * 默認(rèn)數(shù)據(jù)源 */ public static final String DEFAULT_DS = "db1"; /** * 數(shù)據(jù)源屬于一個(gè)公共的資源 * 采用ThreadLocal可以保證在多線程情況下線程隔離 */ private static final ThreadLocal<String> contextHolder = new ThreadLocal<>(); /** * 設(shè)置數(shù)據(jù)源名 * @param dbType */ public static void setDB(String dbType) { contextHolder.set(dbType); } /** * 獲取數(shù)據(jù)源名 * @return */ public static String getDB() { return (contextHolder.get()); } /** * 清除數(shù)據(jù)源名 */ public static void clearDB() { contextHolder.remove(); } }
4、啟動(dòng)
(1)啟動(dòng)類中配置移除默認(rèn)的數(shù)據(jù)庫(kù)配置類
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; //移除默認(rèn)數(shù)據(jù)庫(kù)配置類 @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}) public class DbDemoApplication { public static void main(String[] args) { SpringApplication.run(DbDemoApplication.class, args); } }
(2)測(cè)試
結(jié)果
- db1庫(kù)
- db2庫(kù)
二、一個(gè)是mysql一個(gè)是postgresql
1、pom依賴新增
<dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> <scope>runtime</scope> </dependency>
2、配置文件application.yml
#端口 server.port: 7788 spring.application.name: bddemo # mysql spring.datasource: #數(shù)據(jù)庫(kù)1 db1: driver-class-name: com.mysql.cj.jdbc.Driver jdbc-url: jdbc:mysql://127.0.0.1:3306/db1?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&useSSL=false username: root password: 123456 #數(shù)據(jù)庫(kù)2 db2: # driver-class-name: com.mysql.cj.jdbc.Driver # jdbc-url: jdbc:mysql://127.0.0.1:3306/db2?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&useSSL=false # username: root # password: 123456 driver-class-name: org.postgresql.Driver jdbc-url: jdbc:postgresql://127.0.0.1:5432/test?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&useSSL=false username: postgres password: 123456 # mybatis mybatis: mapper-locations: classpath:mapper/*Mapper.xml type-aliases-package: ccom.example.demo.*.entity
注意:
之前都是mysql的庫(kù),所以驅(qū)動(dòng)在上面
現(xiàn)在因?yàn)閿?shù)據(jù)庫(kù)的產(chǎn)品不一樣,所以驅(qū)動(dòng)類名稱放在下面單獨(dú)配置(有些人真完全不會(huì)變通,哎)
3、測(cè)試
插入:pg數(shù)據(jù)庫(kù)的主鍵自增mybatis還有點(diǎn)難搞,我們直接配置id插入
- mysql:
- pg:
- 查詢:
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
java對(duì)同一個(gè)文件進(jìn)行讀寫(xiě)操作方法
在本篇文章里我們給大家詳細(xì)講述了java對(duì)同一個(gè)文件進(jìn)行讀寫(xiě)操作的方法和知識(shí)點(diǎn),需要的朋友們可以參考學(xué)習(xí)下。2018-10-10Mybatis執(zhí)行流程、緩存原理及相關(guān)面試題匯總
最近剛學(xué)完MyBatis,趁著大好機(jī)會(huì),總結(jié)一下它的執(zhí)行流程,面試也愛(ài)問(wèn)這個(gè),下面這篇文章主要給大家介紹了關(guān)于Mybatis執(zhí)行流程、緩存原理及相關(guān)面試題的相關(guān)資料,需要的朋友可以參考下2022-02-02Spring boot2X Consul如何使用Feign實(shí)現(xiàn)服務(wù)調(diào)用
這篇文章主要介紹了spring boot2X Consul如何使用Feign實(shí)現(xiàn)服務(wù)調(diào)用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-12-12SSH框架網(wǎng)上商城項(xiàng)目第20戰(zhàn)之在線支付平臺(tái)
這篇文章主要為大家詳細(xì)介紹了SSH框架網(wǎng)上商城項(xiàng)目第20戰(zhàn)之在線支付平臺(tái),關(guān)于第三方支付的內(nèi)容從本文開(kāi)始,感興趣的小伙伴們可以參考一下2016-06-06在SpringBoot項(xiàng)目中實(shí)現(xiàn)讀寫(xiě)分離的流程步驟
SpringBoot作為一種快速開(kāi)發(fā)框架,廣泛應(yīng)用于Java項(xiàng)目中,在一些大型應(yīng)用中,數(shù)據(jù)庫(kù)的讀寫(xiě)分離是提升性能和擴(kuò)展性的一種重要手段,本文將介紹如何在SpringBoot項(xiàng)目中優(yōu)雅地實(shí)現(xiàn)讀寫(xiě)分離,并通過(guò)適當(dāng)?shù)拇a插入,詳細(xì)展開(kāi)實(shí)現(xiàn)步驟,同時(shí)進(jìn)行拓展和分析2023-11-11mybatis配置mapper-locations位置的三種方式小結(jié)
這篇文章主要給大家介紹了關(guān)于mybatis配置mapper-locations位置的三種方式,Mybatis-Plus的初衷是為了簡(jiǎn)化開(kāi)發(fā),而不建議開(kāi)發(fā)者自己寫(xiě)SQL語(yǔ)句的,但是有時(shí)客戶需求比較復(fù)雜,需要的朋友可以參考下2023-08-08