Dynamic+Mybatis-plus動(dòng)態(tài)數(shù)據(jù)源的實(shí)踐指南
MyBatis-Plus與動(dòng)態(tài)數(shù)據(jù)源集成
動(dòng)態(tài)數(shù)據(jù)源集成的基本概念
傳統(tǒng)數(shù)據(jù)源與動(dòng)態(tài)數(shù)據(jù)源的區(qū)別
在傳統(tǒng)的企業(yè)級應(yīng)用中,數(shù)據(jù)源通常是靜態(tài)配置的。這意味著,應(yīng)用啟動(dòng)時(shí)會(huì)讀取配置文件中的數(shù)據(jù)源信息,并在整個(gè)生命周期內(nèi)使用這一數(shù)據(jù)源。這種模式適用于單體應(yīng)用,因?yàn)閱误w應(yīng)用通常只需要連接到單一數(shù)據(jù)庫。
然而,隨著微服務(wù)架構(gòu)的流行,企業(yè)開始拆分大型單體應(yīng)用為一系列小的、自治的微服務(wù),每個(gè)微服務(wù)通常都有自己的數(shù)據(jù)庫。這導(dǎo)致動(dòng)態(tài)數(shù)據(jù)源的需求增加,因?yàn)橐粋€(gè)微服務(wù)可能需要根據(jù)運(yùn)行時(shí)的條件(如服務(wù)調(diào)用的上下文),動(dòng)態(tài)切換到不同的數(shù)據(jù)庫。動(dòng)態(tài)數(shù)據(jù)源的集成使得應(yīng)用能夠靈活地管理和切換多個(gè)數(shù)據(jù)源,而無需重啟服務(wù)。
動(dòng)態(tài)數(shù)據(jù)源與傳統(tǒng)數(shù)據(jù)源的最大區(qū)別在于其靈活性和適應(yīng)性,它允許開發(fā)者在運(yùn)行時(shí)動(dòng)態(tài)切換數(shù)據(jù)源,以適應(yīng)業(yè)務(wù)需求的變化。這一點(diǎn)在微服務(wù)架構(gòu)中尤為重要,因?yàn)樗苡行幚矸?wù)之間的數(shù)據(jù)交互和事務(wù)管理。
動(dòng)態(tài)數(shù)據(jù)源集成的需求背景分析
隨著業(yè)務(wù)復(fù)雜性的提升,企業(yè)對于應(yīng)用的可擴(kuò)展性和靈活性要求越來越高。在多租戶系統(tǒng)、跨系統(tǒng)數(shù)據(jù)查詢、數(shù)據(jù)集中處理等場景下,系統(tǒng)需要連接多個(gè)數(shù)據(jù)庫,且這些數(shù)據(jù)庫可能分布在不同的物理位置或云環(huán)境中。
動(dòng)態(tài)數(shù)據(jù)源集成正是為了解決這些場景下遇到的問題。在多租戶應(yīng)用中,不同租戶的數(shù)據(jù)存儲(chǔ)在不同的數(shù)據(jù)庫實(shí)例中,而動(dòng)態(tài)數(shù)據(jù)源的集成使得系統(tǒng)能夠?yàn)槊總€(gè)租戶切換到正確的數(shù)據(jù)源,避免了數(shù)據(jù)錯(cuò)亂。
在跨系統(tǒng)數(shù)據(jù)交互中,動(dòng)態(tài)數(shù)據(jù)源能實(shí)現(xiàn)系統(tǒng)間的高效數(shù)據(jù)同步和查詢。例如,在微服務(wù)架構(gòu)中,服務(wù)A可能需要查詢服務(wù)B的數(shù)據(jù),而服務(wù)B使用的數(shù)據(jù)庫與服務(wù)A不同。通過動(dòng)態(tài)數(shù)據(jù)源集成,服務(wù)A可以在不關(guān)心服務(wù)B數(shù)據(jù)庫細(xì)節(jié)的情況下,透明地訪問所需數(shù)據(jù)。
總之,動(dòng)態(tài)數(shù)據(jù)源集成的需求背景主要來自于業(yè)務(wù)的多變性和復(fù)雜性,其目標(biāo)是提升系統(tǒng)的靈活性和維護(hù)性,同時(shí)降低系統(tǒng)的耦合度,提高數(shù)據(jù)處理的效率和安全性。
本文介紹Dynamic+Mybatis-plus動(dòng)態(tài)數(shù)據(jù)源,具體內(nèi)容如下所示:
在進(jìn)行配置之前需要引入pom依賴
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.24</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.24</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot3-starter</artifactId>
<version>4.3.1</version>
</dependency>1、首先需要進(jìn)行配置yml文件,如下
spring:
application:
name: project-name
datasource:
dynamic:
type: com.alibaba.druid.pool.DruidDataSource
# 設(shè)置默認(rèn)的數(shù)據(jù)源或者數(shù)據(jù)源組,默認(rèn)值即為 master
primary: master
# 嚴(yán)格匹配數(shù)據(jù)源,默認(rèn)false,true未匹配到指定數(shù)據(jù)源時(shí)拋異常,false使用默認(rèn)數(shù)據(jù)源
strict: true
datasource:
# 主庫數(shù)據(jù)源
master:
type: ${spring.datasource.type}
driver-class-name:
url:
username:
password:
# 從庫數(shù)據(jù)源
slave:
type: ${spring.datasource.type}
driver-class-name:
url:
username:
password:
druid:
inital-size: 10
#最大連接數(shù)
max-active: 50
#最小連接數(shù)
min-idle: 10
#獲取鏈接等待超時(shí)時(shí)間
max-wait: 5000
pool-prepared-statements: true #是否緩存preparedStatement,也就是PSCache。PSCache對支持游標(biāo)的數(shù)據(jù)庫性能提升巨大,比如說oracle。在mysql下建議關(guān)閉。
max-pool-prepared-statement-per-connection-size: 20
validation-query: SELECT 1 FROM DUAL
validation-query-timeout: 20000
test-on-borrow: false #申請連接時(shí)執(zhí)行validationQuery檢測連接是否有效,做了這個(gè)配置會(huì)降低性能。
test-on-return: false #歸還連接時(shí)執(zhí)行validationQuery檢測連接是否有效,做了這個(gè)配置會(huì)降低性能。
test-while-idle: true #建議配置為true,不影響性能,并且保證安全性。申請連接的時(shí)候檢測,如果空閑時(shí)間大于timeBetweenEvictionRunsMillis,執(zhí)行validationQuery檢測連接是否有效。
time-between-eviction-runs-millis: 60000 #配置間隔多久才進(jìn)行一次檢測,檢測需要關(guān)閉的空閑連接,單位是毫秒
min-evictable-idle-time-millis: 300000 #一個(gè)連接在池中最小生存的時(shí)間,單位是毫秒
max-evictable-idle-time-millis: 900000 # 配置一個(gè)連接在池中最大生存的時(shí)間,單位是毫秒
#StatViewServlet配置。(因?yàn)楸┞兜谋O(jiān)控信息比較敏感,支持密碼加密和訪問ip限定)
web-stat-filter:
enableed: true
stat-view-servlet:
enabled: true
url-pattern: /druid/*
#可以增加訪問賬號(hào)密碼【去掉注釋就可以】
#login-username: admin
#login-password: admin
filter:
stat:
enabled: true
# 慢SQL 記錄
log-slow-sql: true
slow-sql-millis: 1000
merge-sql: true
wall:
config:
multi-statement-allow: true2、再配置完之后,添加Druid配置(博主使用的是druid)
package com.guyk.common.config;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
@Slf4j
@Configuration
@EnableTransactionManagement(order = 1)
// @MapperScan(basePackages = {"com.guyk.dao.mapper"})
public class DruidConfig {
@Bean
@Primary
public DataSource dataSource() {
DynamicDataSource dynamicDataSource = new DynamicDataSource();
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put("master", masterDataSource());
targetDataSources.put("slave", slaveDataSource());
dynamicDataSource.setDefaultTargetDataSource(masterDataSource());
dynamicDataSource.setTargetDataSources(targetDataSources);
return dynamicDataSource;
}
@Bean
@ConfigurationProperties("spring.datasource.dynamic.datasource.master")
public DataSource masterDataSource() {
return DruidDataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties("spring.datasource.dynamic.datasource.slave")
public DataSource slaveDataSource() {
return DruidDataSourceBuilder.create().build();
}
}3、配置上下文來管理數(shù)據(jù)源
package com.guyk.common.config;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class DataSourceContextHolder {
private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
// 設(shè)置數(shù)據(jù)源類型
public static void setDataSourceType(String dataSourceType) {
log.info("切換數(shù)據(jù)源: {}", dataSourceType);
contextHolder.set(dataSourceType);
}
// 獲取當(dāng)前線程的數(shù)據(jù)源類型
public static String getDataSourceType() {
return contextHolder.get();
}
// 清除當(dāng)前線程的數(shù)據(jù)源類型
public static void clearDataSourceType() {
String current = getDataSourceType();
contextHolder.remove();
log.info("清除數(shù)據(jù)源: {}", current);
}
}4、添加一個(gè)注解,用來在方法、類上使用,進(jìn)行數(shù)據(jù)源的切換,并設(shè)置默認(rèn)數(shù)據(jù)源未master
package com.guyk.common.annotation;
import java.lang.annotation.*;
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
/**
* 數(shù)據(jù)源名稱
*/
String value() default "master";
}5、使用AOP切換數(shù)據(jù)源時(shí),獲取@DataSource注解的值,并將數(shù)據(jù)源類型保存到DataSourceContextHolder中。
package com.guyk.common.config;
import com.hhubrain.common.annotation.DataSource;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
@Slf4j
@Aspect
@Component
@Order(-1)
public class DataSourceAspect {
/**
* 攔截帶有@DataSource注解的方法
*/
@Around("@annotation(com.hhubrain.common.annotation.DataSource)")
public Object around(ProceedingJoinPoint point) throws Throwable {
MethodSignature signature = (MethodSignature) point.getSignature();
Method method = signature.getMethod();
// 獲取注解中的數(shù)據(jù)源類型
DataSource dataSourceAnnotation = method.getAnnotation(DataSource.class);
String dataSourceType = dataSourceAnnotation.value();
// 獲取當(dāng)前線程的數(shù)據(jù)源類型
String oldDataSource = DataSourceContextHolder.getDataSourceType();
log.info("數(shù)據(jù)源切換: {} -> {}", oldDataSource, dataSourceType);
// 切換數(shù)據(jù)源
DataSourceContextHolder.setDataSourceType(dataSourceType);
try {
return point.proceed();
} finally {
// 還原數(shù)據(jù)源
if (oldDataSource != null) {
DataSourceContextHolder.setDataSourceType(oldDataSource);
log.info("恢復(fù)數(shù)據(jù)源: {}", oldDataSource);
} else {
DataSourceContextHolder.clearDataSourceType(); // 清除線程數(shù)據(jù)源
log.info("清除數(shù)據(jù)源上下文");
}
}
}
}6、在啟動(dòng)類上排除DataSourceAutoConfiguration.class
到此這篇關(guān)于Dynamic+Mybatis-plus動(dòng)態(tài)數(shù)據(jù)源的文章就介紹到這了,更多相關(guān)Dynamic Mybatis-plus數(shù)據(jù)源內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- MyBatis-Plus與Druid結(jié)合Dynamic-datasource實(shí)現(xiàn)多數(shù)據(jù)源操作數(shù)據(jù)庫的示例
- Spring Boot 3.4.0 結(jié)合 Mybatis-plus 實(shí)現(xiàn)動(dòng)態(tài)數(shù)據(jù)源的完整方案
- mybatis-plus動(dòng)態(tài)數(shù)據(jù)源讀寫分離方式
- Mybatis-Plus中使用@DS注解動(dòng)態(tài)選擇數(shù)據(jù)源的源碼解讀
- 解決mybatis-plus動(dòng)態(tài)數(shù)據(jù)源切換不生效的問題
- mybatis-plus @DS實(shí)現(xiàn)動(dòng)態(tài)切換數(shù)據(jù)源原理
- MyBatis-Plus 集成動(dòng)態(tài)多數(shù)據(jù)源的實(shí)現(xiàn)示例
相關(guān)文章
一文淺析Java金額計(jì)算用long還是BigDecimal
在Java后端開發(fā)中處理與錢有關(guān)的業(yè)務(wù)時(shí),確保金額計(jì)算的準(zhǔn)確性和避免錯(cuò)誤非常重要,這篇文章主要給大家介紹了關(guān)于Java中處理金額計(jì)算之使用Long還是BigDecimal的相關(guān)資料,希望對大家有所幫助2025-12-12
Graceful Response 構(gòu)建 Spring Boot 響應(yīng)
Graceful Response是一個(gè)Spring Boot技術(shù)棧下的優(yōu)雅響應(yīng)處理器,提供一站式統(tǒng)一返回值封裝、全局異常處理、自定義異常錯(cuò)誤碼等功能,本文介紹Graceful Response 構(gòu)建 Spring Boot 下優(yōu)雅的響應(yīng)處理,感興趣的朋友一起看看吧2024-01-01
java數(shù)據(jù)結(jié)構(gòu)與算法之插入算法實(shí)現(xiàn)數(shù)值排序示例
這篇文章主要介紹了java數(shù)據(jù)結(jié)構(gòu)與算法之插入算法實(shí)現(xiàn)數(shù)值排序的方法,結(jié)合簡單實(shí)例形式分析了插入算法的節(jié)點(diǎn)操作與排序相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下2016-08-08
IntelliJ IDEA快速查看某個(gè)類/接口的子類或父類
本文主要介紹了IntelliJ IDEA快速查看某個(gè)類/接口的子類或父類,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-07-07

