sharding-jdbc 兼容 MybatisPlus動態(tài)數(shù)據(jù)源的配置方法
背景:之前的項目做讀寫分離的時候用的 MybatisPlus的動態(tài)數(shù)據(jù)做的,很多地方使用的@DS直接指定的讀庫或者寫庫實現(xiàn)的業(yè)務(wù);隨著表數(shù)據(jù)量越來越大,現(xiàn)在打算把比較大的表進行水平拆分,準備使用 ShardingJDBC實現(xiàn),但是發(fā)現(xiàn)兩者配合起來并不是那么順利,網(wǎng)上大部分文章都是直接把整個Sharding的數(shù)據(jù)源當(dāng)成MybatisPlus的一個數(shù)據(jù)源,那么在原本@DS上面指定的數(shù)據(jù)源就無法直接使用Sharding的分庫等邏輯,所以我研究了一下源碼,實現(xiàn)了這一邏輯,給后面有需要的朋友提供一個案例,避免浪費不必要的時間
一. 版本選擇
目前ShardingJDBC主要有兩個版本,一個是ShardingJDBC早期版本,一個是ShardingSphere項目中的ShardingSphere-JDBC
- Sharding-JDBC:Sharding-JDBC 最初由當(dāng)時的項目發(fā)起人在2016年發(fā)布。它最早作為一個輕量級的 JDBC 層解決方案,旨在解決數(shù)據(jù)庫分片和讀寫分離的問題。
- ShardingSphere:ShardingSphere 項目是由 Sharding-JDBC 項目發(fā)展而來的,并在2018年正式發(fā)布。Apache ShardingSphere 致力于構(gòu)建更為完整的分布式數(shù)據(jù)庫管理生態(tài)系統(tǒng),包含了 Sharding-JDBC、Sharding-Proxy 和 Sharding-Sidecar等多個組件。
目前獨立的ShardingJDBC已經(jīng)停更,使用到的最多的版本是 4.1.1
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-boot-starter</artifactId>
<version>4.1.1</version>
</dependency>
ShardingSphere項目目前一直處于更新迭代中,ShardingSphere-JDBC 是通過ShardingJDBC 更新迭代過來的,在原有代碼的基礎(chǔ)進行了一些優(yōu)化和新功能加入,對于開發(fā)者而言,主要是參數(shù)的配置發(fā)生了一些調(diào)整。但是參數(shù)的作用和配置方式和以前一樣;
這里我為了方便以后會使用到新特性,我直接使用的是 ShardingSphere-JDBC 5.2.1
官方幫助文檔:https://www.bookstack.cn/read/shardingsphere-5.1.0-zh/ecf18b21ab3f559c.md
<dependency> <groupId>org.apache.shardingsphere</groupId> <artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId> <version>5.2.1</version> </dependency>
二. 項目依賴
案例全部的 Maven依賴如下:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>3.4.5</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.0</version>
</dependency>
<!-- 讀寫分離 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>3.3.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>
<!--Shardingjdbc-->
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
<version>5.2.1</version>
<exclusions>
<exclusion>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 添加正確版本的 SnakeYAML shardingsphere-jdbc里面的依賴版本有問題,會報錯-->
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>1.33</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>三. 參數(shù)配置
application.yml 配置
server:
port: 8080
mybatis-plus:
mapper-locations: classpath*:mybatis/*.xml
type-aliases-package: com.game.sharding.dto
configuration:
map-underscore-to-camel-case: false
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
spring:
application:
name: sharding-jdbc-test
sharding-sphere:
datasource:
names: master,write,read,read2
master:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://127.0.0.1:3306/game_dev?characterEncoding=utf-8&useSSL=false&allowMultiQueries=true&autoReconnect=true&serverTimezone=Asia/Shanghai
username: root
password: 123456
write:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://127.0.0.1:3306/game_dev?characterEncoding=utf-8&useSSL=false&allowMultiQueries=true&autoReconnect=true&serverTimezone=Asia/Shanghai
username: root
password: 123456
read:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://127.0.0.1:3306/game_dev_read?characterEncoding=utf-8&useSSL=false&allowMultiQueries=true&autoReconnect=true&serverTimezone=Asia/Shanghai
username: root
password: 123456
read2:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://127.0.0.1:3306/game_dev_read?characterEncoding=utf-8&useSSL=false&allowMultiQueries=true&autoReconnect=true&serverTimezone=Asia/Shanghai
username: root
password: 123456
rules:
sharding:
tables:
team_msg:
## 這里的customer-ds是下面配置的讀寫分離的數(shù)據(jù)源名稱
actual-data-nodes: customer-ds.team_msg_${0..1}
table-strategy:
standard:
sharding-column: id
sharding-algorithm-name: msg-id # 對應(yīng)下面的sharding-algorithms
sharding-algorithms:
## 注意這里名稱(例如msg-id)不能用下劃線,會加載不了下面的參數(shù)導(dǎo)致啟動報錯
msg-id:
type: INLINE
props:
## 使用id取模算法
algorithm-expression: team_msg_${id % 2}
## 讀寫分離相關(guān)
readwrite-splitting:
data-sources:
customer-ds:
load-balancer-name: customer-lb
static-strategy:
write-data-source-name: master
read-data-source-names: read,read2,write
load-balancers:
customer-lb:
## 使用自定義的復(fù)雜均衡算法
type: CUSTOM
props:
# 顯示處理之后的真實sql
sql-show: true四. 代碼配置
最關(guān)鍵的配置就是需要把MybatisPlus的數(shù)據(jù)源注冊為使用 shardingsphere-jdbc 的數(shù)據(jù)源,并且保證數(shù)據(jù)源的名稱和原來MybatisPlus的數(shù)據(jù)源一致,shardingSphereDataSource里面其實有一個Map保存了application.yml中所有配置的數(shù)據(jù)源,這里主要是為了方便后續(xù)使用@DS做動態(tài)數(shù)據(jù)源切換,所以把同一個ShardingSphere的數(shù)據(jù)庫注冊為4個動態(tài)數(shù)據(jù)源,避免使用@DS找不到對應(yīng)的數(shù)據(jù)源;
import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
import com.baomidou.dynamic.datasource.provider.AbstractDataSourceProvider;
import com.baomidou.dynamic.datasource.provider.DynamicDataSourceProvider;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceAutoConfiguration;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceProperties;
import org.apache.commons.lang3.StringUtils;
import org.apache.shardingsphere.driver.jdbc.adapter.AbstractDataSourceAdapter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Primary;
import javax.annotation.Resource;
import javax.sql.DataSource;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
@Configuration
@AutoConfigureBefore({DynamicDataSourceAutoConfiguration.class, SpringBootConfiguration.class})
public class MyDataSourceConfiguration {
/**
* mybatisplus 動態(tài)數(shù)據(jù)源配置項
*/
@Autowired
private DynamicDataSourceProperties properties;
/**
* shardingjdbc的數(shù)據(jù)源
*/
@Lazy
@Resource(name = "shardingSphereDataSource")
private AbstractDataSourceAdapter shardingSphereDataSource;
@Value("${spring.sharding-sphere.datasource.names}")
private String shardingDataSourceNames;
/**
* 注冊動態(tài)數(shù)據(jù)源 這里非常關(guān)鍵,因為我們需要用到@DS注解配置動態(tài)選擇數(shù)據(jù)源,同上又要讓選擇的數(shù)據(jù)源使用shardingjdbc的數(shù)據(jù)源
* 所以,這里需要動態(tài)的把所有的數(shù)據(jù)源都注冊為 shardingjdbc的數(shù)據(jù)源
*/
@Bean
public DynamicDataSourceProvider dynamicDataSourceProvider() {
if (StringUtils.isBlank(shardingDataSourceNames)) {
throw new RuntimeException("配置 spring.sharding-sphere.datasource.names 不能為空");
}
String[] names = shardingDataSourceNames.split(",");
return new AbstractDataSourceProvider() {
@Override
public Map<String, DataSource> loadDataSources() {
Map<String, DataSource> dataSourceMap = new HashMap<>();
Arrays.stream(names).forEach(name -> dataSourceMap.put(name, shardingSphereDataSource));
return dataSourceMap;
}
};
}
/**
* 將動態(tài)數(shù)據(jù)源設(shè)置為首選數(shù)據(jù)源
*/
@Primary
@Bean
public DataSource dataSource(DynamicDataSourceProvider dynamicDataSourceProvider) {
DynamicRoutingDataSource dataSource = new DynamicRoutingDataSource();
dataSource.setPrimary(properties.getPrimary());
dataSource.setStrict(properties.getStrict());
dataSource.setStrategy(properties.getStrategy());
dataSource.setProvider(dynamicDataSourceProvider);
dataSource.setP6spy(properties.getP6spy());
dataSource.setSeata(properties.getSeata());
return dataSource;
}
}五. 自定義ShardingSphere中的復(fù)雜均衡算法
shardingsphere中的負載均衡需要實現(xiàn)ReadQueryLoadBalanceAlgorithm接口并在getType方法中返回自定義的算法名稱,官方自帶的又RoundRobinReadQueryLoadBalanceAlgorithm,RandomReadQueryLoadBalanceAlgorithm等,這里我們必須自定義算法才能兼容@DS注解實現(xiàn)自由切換數(shù)據(jù)源;
ShardingSphere使用的是SPI機制加載的,對應(yīng)的加載源碼部分如下:

所以如果我們要讓自定義的ReadQueryLoadBalanceAlgorithm類生效,需要在項目中的 META-INF的services文件夾中創(chuàng)建org.apache.shardingsphere.readwritesplitting.spi.ReadQueryLoadBalanceAlgorithm 文件,并且把自定義的類填入該文件中
源碼中的配置如下:

那么我們按照源碼的配置直接在自己的項目中創(chuàng)建即可

最后自定義的CustomLoadBalanceAlgorithm 實現(xiàn)
public class CustomLoadBalanceAlgorithm implements ReadQueryLoadBalanceAlgorithm {
private Properties props;
public CustomLoadBalanceAlgorithm() {
}
@Override
public void init(Properties props) {
this.props = props;
}
/**
* 獲取數(shù)據(jù)源
*
* @param name 數(shù)據(jù)源名稱(ShardingJDBC使用的)
* @param writeDataSourceName 寫數(shù)據(jù)源名稱
* @param readDataSourceNames 所有配置的復(fù)雜均衡中讀數(shù)據(jù)源名稱
* @param context 事務(wù)上下文對象,可以獲取context.isInTransaction() 判斷是否需要事務(wù),可通過這個來判斷是否使用 寫數(shù)據(jù)源
* @return java.lang.String
*/
@Override
public String getDataSource(String name, String writeDataSourceName, List<String> readDataSourceNames, TransactionConnectionContext context) {
// 獲取當(dāng)前MybatisPlus指定的數(shù)據(jù)源
String dsKey = DynamicDataSourceContextHolder.peek();
if (StringUtils.isNotBlank(dsKey)) {
if (writeDataSourceName.equals(dsKey)) {
return dsKey;
}
if (readDataSourceNames.contains(dsKey)) {
return dsKey;
}
throw new RuntimeException("@DS 配置錯誤,當(dāng)前數(shù)據(jù)源[" + dsKey + "]不在SharingJDBC數(shù)據(jù)源列表[" + readDataSourceNames + "]中");
}
return writeDataSourceName;
}
@Override
public String getType() {
return "CUSTOM";
}
@Override
public boolean isDefault() {
return true;
}
@Override
@Generated
public Properties getProps() {
return this.props;
}
}那么此時你的ShardingSphere就已經(jīng)完全適配之前MybatisPlus動態(tài)數(shù)據(jù)源了

六. 源碼
Gitee: https://gitee.com/luowenjie98/sharing-sphere-mybatisplus-demo
到此這篇關(guān)于sharding-jdbc 兼容 MybatisPlus的動態(tài)數(shù)據(jù)源的文章就介紹到這了,更多相關(guān)sharding-jdbc MybatisPlus的動態(tài)數(shù)據(jù)源內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot?項目打成?jar后加載外部配置文件的操作方法
這篇文章主要介紹了SpringBoot?項目打成?jar后加載外部配置文件的操作方法,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-03-03
Spring?Retry?實現(xiàn)樂觀鎖重試實踐記錄
本文介紹了在秒殺商品SKU表中使用樂觀鎖和MybatisPlus配置樂觀鎖的方法,并分析了測試環(huán)境和生產(chǎn)環(huán)境的隔離級別對樂觀鎖的影響,通過簡單驗證,展示了在可重復(fù)讀和讀已提交隔離級別下的不同行為,感興趣的朋友一起看看吧2025-03-03
JVM參數(shù)NativeMemoryTracking的使用
本文主要介紹了JVM參數(shù)NativeMemoryTracking的使用,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2025-01-01
springBoot中的CORS跨域注解@CrossOrigin詳解
這篇文章主要介紹了springBoot中的CORS跨域注解@CrossOrigin詳解,通常,服務(wù)于?JS?的主機(例如?example.com)與服務(wù)于數(shù)據(jù)的主機(例如?api.example.com)是不同的,在這種情況下,CORS?可以實現(xiàn)跨域通信,需要的朋友可以參考下2023-12-12

