1、目標(biāo)
1、實現(xiàn)動態(tài)切換數(shù)據(jù)源
2、實現(xiàn)配置多數(shù)據(jù)源
3、實現(xiàn)讀寫分離也可以用多數(shù)據(jù)源方式
4、選擇
dynamic-datasource集成了很多ORM的框架,其中,使用比較多的是druid,但有一些東西開始收費了
druid也可以自行配置,配置多了點
目前版本只支持單一位置加載數(shù)據(jù)源(只能從配置文件或者自定義加載數(shù)據(jù)源),不能多位置加載數(shù)據(jù)源
目前版本未實現(xiàn)動態(tài)添加數(shù)據(jù)源(在切換數(shù)據(jù)源時,不存在的數(shù)據(jù)源在數(shù)據(jù)庫查詢,添加進(jìn)數(shù)據(jù)源連接池)
2、源代碼
springboot版本:2.3.1.RELEASE
dynamic-datasource版本:3.5.1
官方文檔:https://www.kancloud.cn/tracy5546/dynamic-datasource/2264611
實現(xiàn)源代碼地址文章來源:http://www.zghlxwxcb.cn/news/detail-487419.html
3、實現(xiàn)
pom.xml配置
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>3.5.1</version>
</dependency>
application.yml配置
spring:
application:
name: microservice-boot-common
# Jackson 配置項
jackson:
# serialization:
# write-dates-as-timestamps: true # 設(shè)置 Date 的格式,使用時間戳
# write-date-timestamps-as-nanoseconds: false # 設(shè)置不使用 nanoseconds 的格式。例如說 1611460870.401,而是直接 1611460870401
# write-durations-as-timestamps: true # 設(shè)置 Duration 的格式,使用時間戳
# fail-on-empty-beans: false # 允許序列化無屬性的 Bean
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
datasource:
dynamic:
primary: master #設(shè)置默認(rèn)的數(shù)據(jù)源或者數(shù)據(jù)源組,默認(rèn)值即為master
strict: false #嚴(yán)格匹配數(shù)據(jù)源,默認(rèn)false. true未匹配到指定數(shù)據(jù)源時拋異常,false使用默認(rèn)數(shù)據(jù)源
datasource:
druid: # Druid 【監(jiān)控】相關(guān)的全局配置
web-stat-filter:
enabled: true
stat-view-servlet:
enabled: true
allow: # 設(shè)置白名單,不填則允許所有訪問
url-pattern: /druid/*
login-username: test
login-password: test
filter:
stat:
enabled: true
log-slow-sql: true # 慢 SQL 記錄
slow-sql-millis: 1000
merge-sql: true
wall:
config:
multi-statement-allow: true
initial-size: 4 # 初始連接數(shù)
min-idle: 4 # 最小連接池數(shù)量
max-active: 50 # 最大連接池數(shù)量
max-wait: 600000 # 配置獲取連接等待超時的時間,單位:毫秒
time-between-eviction-runs-millis: 60000 # 配置間隔多久才進(jìn)行一次檢測,檢測需要關(guān)閉的空閑連接,單位:毫秒
min-evictable-idle-time-millis: 300000 # 配置一個連接在池中最小生存的時間,單位:毫秒
max-evictable-idle-time-millis: 900000 # 配置一個連接在池中最大生存的時間,單位:毫秒
validation-query: SELECT 1 # 配置檢測連接是否有效
test-while-idle: true
test-on-borrow: false
test-on-return: false
name: master
master:
driver-class-name: com.mysql.jdbc.Driver # 3.2.0開始支持SPI可省略此配置
url: jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&allowPublicKeyRetrieval=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&allowMultiQueries=true&nullCatalogMeansCurrent=true
username: root
password: 123456
slave_1:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/test2?useUnicode=true&allowPublicKeyRetrieval=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&allowMultiQueries=true&nullCatalogMeansCurrent=true
username: root
password: 123456
# MyBatis Plus 的配置項
mybatis-plus:
configuration:
map-underscore-to-camel-case: true # 雖然默認(rèn)為 true ,但是還是顯示去指定下。
global-config:
db-config:
# id-type: NONE # “智能”模式,基于 IdTypeEnvironmentPostProcessor + 數(shù)據(jù)源的類型,自動適配成 AUTO、INPUT 模式。
id-type: AUTO # 自增 ID,適合 MySQL 等直接自增的數(shù)據(jù)庫
# id-type: INPUT # 用戶輸入 ID,適合 Oracle、PostgreSQL、Kingbase、DB2、H2 數(shù)據(jù)庫
# id-type: ASSIGN_ID # 分配 ID,默認(rèn)使用雪花算法。注意,Oracle、PostgreSQL、Kingbase、DB2、H2 數(shù)據(jù)庫時,需要去除實體類上的 @KeySequence 注解
logic-delete-value: 1 # 邏輯已刪除值(默認(rèn)為 1)
logic-not-delete-value: 0 # 邏輯未刪除值(默認(rèn)為 0)
logic-delete-field: deleted_tag
table-prefix: t_
banner: false
type-aliases-package: org.lwd.microservice.boot.common.dao
mapper-locations: classpath*:/mapper/**/*.xml
手動切換
@DS("slave_1")
@Override
public BaseResult<TenantDataSourceDTO> getTenantDataSourceByPk(String pk) {
BaseResult<TenantDataSourceDTO> baseResult = BaseResult.success();
TenantDataSource domain = this.getById(pk);
baseResult.setData(TenantDataSourceConvertor.INSTANCE.toDTO(domain));
return baseResult;
}
動態(tài)切換-aop模式
package org.lwd.microservice.boot.common.aop;
import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
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.annotation.Pointcut;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
/**
* 數(shù)據(jù)源攔截-aop模式
*
* @author weidong
* @version V1.0.0
* @since 2023/6/13
*/
@Order(1)
@Aspect
@EnableAspectJAutoProxy(proxyTargetClass = true)
@Component
@Slf4j
public class DataSourceChangeAdvisor {
/**
* 按需設(shè)置需要切換的模式
*/
@Pointcut("@within(org.springframework.web.bind.annotation.RestController)")
public void pointDataSource() {
}
/**
* 需要定義模塊的規(guī)則
* @param joinPoint
* @return
* @throws Throwable
*/
@Around("pointDataSource()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
try {
//獲取當(dāng)前請求對象
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (attributes != null) {
HttpServletRequest request = attributes.getRequest();
String uri = request.getRequestURI();
//String url = request.getRequestURL().toString();
String[] uriArr = uri.split("/");
if (uriArr[uriArr.length - 1].equals("detail")) {
log.info("datasource1------");
DynamicDataSourceContextHolder.push("master");
} else {
log.info("datasource2------");
DynamicDataSourceContextHolder.push("slave_1");
}
}
} catch (Exception e) {
log.error("日志攔截異常", e);
} finally {
return joinPoint.proceed();
}
}
}
動態(tài)切換-攔截器模式
package org.lwd.microservice.boot.common.interceptor;
import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 動態(tài)數(shù)據(jù)源-攔截器模式-需要自定義規(guī)則
* @author weidong
* @version V1.0.0
* @since 2023/6/16
*/
@Slf4j
public class DynamicDataSourceChangeInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String uri = request.getRequestURI();
//String url = request.getRequestURL().toString();
String[] uriArr = uri.split("/");
if (uriArr[uriArr.length - 1].equals("detail")) {
log.info("interceptor datasource1------");
DynamicDataSourceContextHolder.push("master");
} else {
log.info("interceptor datasource2------");
DynamicDataSourceContextHolder.push("slave_1");
}
return true;
}
}
package org.lwd.microservice.boot.common.interceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* mvc攔截器-自定義攔截路徑
* @author weidong
* @version V1.0.0
* @since 2023/6/16
*/
@Configuration
public class DynamicDataSourceChangeInterceptorConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
//如果攔截全部可以設(shè)置為 /**
String [] path = {"/tenantDataSource/**"};
//不需要攔截的接口路徑
String [] excludePath = {};
DynamicDataSourceChangeInterceptor dynamicDataSourceChangeInterceptor = new DynamicDataSourceChangeInterceptor();
registry.addInterceptor(dynamicDataSourceChangeInterceptor).addPathPatterns(path).excludePathPatterns(excludePath);
}
}
4、總結(jié)
這種方式是配置文件的方式,可對立的配置文件配置多個數(shù)據(jù)源,
但是動態(tài)增加或者減少數(shù)據(jù)源,需要自己實現(xiàn)一部分代碼,看下一篇文章文章來源地址http://www.zghlxwxcb.cn/news/detail-487419.html
外傳
?? 原創(chuàng)不易,如若本文能夠幫助到您的同學(xué)
?? 支持我:關(guān)注我+點贊??+收藏??
?? 留言:探討問題,看到立馬回復(fù)
?? 格言:己所不欲勿施于人 揚帆起航、游歷人生、永不言棄!??
到了這里,關(guān)于springboot dynamic-datasource 實現(xiàn)動態(tài)切換數(shù)據(jù)源-多租戶-配置文件切換-基于dynamic-datasource的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!