SpringBoot多數(shù)據(jù)源切換實(shí)現(xiàn)代碼(Mybaitis)
前言
但是在實(shí)際業(yè)務(wù)場(chǎng)景中,數(shù)據(jù)量迅速增長(zhǎng),一個(gè)庫(kù)一個(gè)表已經(jīng)滿足不了我們的需求的時(shí)候,我們就會(huì)考慮分庫(kù)分表的操作,在springboot中如何實(shí)現(xiàn)多數(shù)據(jù)源,動(dòng)態(tài)數(shù)據(jù)源切換,讀寫分離等操作。 當(dāng)你看到這篇文件那么你幸運(yùn)了,下面直接提供終極通用版代碼
如果是非Mybaitis的那么可以進(jìn)行參照,原理都差不多
配置文件(YML)
spring:
datasource:
default-db-key: voidme
multi-db:
- voidme:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: root
url: jdbc:mysql://192.168.42.153:3306/voidme?characterEncoding=utf8&autoReconnect=true&failOverReadOnly=false&maxReconnects=10&useSSL=false
- xcdef:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: root
url: jdbc:mysql://192.168.42.153:3306/xcdef?characterEncoding=utf8&autoReconnect=true&failOverReadOnly=false&maxReconnects=10&useSSL=false
mybatis:
#1.classpath:只會(huì)到你的classes路徑中查找找文件。
#2.classpath*:不僅會(huì)到classes路徑,還包括jar文件中(classes路徑)進(jìn)行查找。
mapper-locations: classpath*:/mapper/**/*Mapper.xml # mapper映射文件位置
type-aliases-package: com.**.entity # 實(shí)體類所在的位置
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #用于控制臺(tái)打印sql語句
map-underscore-to-camel-case: true #開啟將帶有下劃線的表字段 映射為駝峰格式的實(shí)體類屬性
核心代碼

DynamicDataSource
這個(gè)類用于獲取數(shù)據(jù)源的(核心)
package com.dynamicdatadource.dynamic;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
public class DynamicDataSource extends AbstractRoutingDataSource {
@Value("${spring.datasource.default-db-key}")
private String defaultDbKey;
@Override
protected Object determineCurrentLookupKey() {
String currentDb = DynamicDataSourceService.currentDb();
if (currentDb == null) {
return defaultDbKey;
}
return currentDb;
}
}DynamicDataSourceService
這個(gè)類是數(shù)據(jù)源切換工具,我們做了線程隔離了所以不用擔(dān)心多線程數(shù)據(jù)源會(huì)混亂的問題
package com.dynamicdatadource.dynamic;
import com.application.ApplicationContextProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.jdbc.DataSourceBuilder;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
public class DynamicDataSourceService {
private static final Logger log = LoggerFactory.getLogger(DynamicDataSourceService.class);
private static final Map<Object, Object> dataSources = new HashMap<>();
private static final ThreadLocal<String> dbKeys = ThreadLocal.withInitial(() -> null);
/**
* 動(dòng)態(tài)添加一個(gè)數(shù)據(jù)源
*
* @param name 數(shù)據(jù)源的key
* @param dataSource 數(shù)據(jù)源對(duì)象
*/
public static void addDataSource(String name, DataSource dataSource) {
DynamicDataSource dynamicDataSource = ApplicationContextProvider.getApplicationContext().getBean(DynamicDataSource.class);
dataSources.put(name, dataSource);
dynamicDataSource.setTargetDataSources(dataSources);
dynamicDataSource.afterPropertiesSet();
log.info("添加了數(shù)據(jù)源:{}",name);
}
/**
* @param name 數(shù)據(jù)源的key
* @param driverClassName 驅(qū)動(dòng)
* @param url 數(shù)據(jù)庫(kù)連接地址
* @param username 數(shù)據(jù)庫(kù)賬戶
* @param password 數(shù)據(jù)庫(kù)密碼
*/
public static void addDataSource(String name, String driverClassName,String url,String username,String password) {
DataSourceBuilder<?> builder = DataSourceBuilder.create();
builder.driverClassName(driverClassName);
builder.username(username);
builder.password(password);
builder.url(url);
addDataSource(name,builder.build());
log.info("添加了數(shù)據(jù)源:{}",name);
}
/**
* 切換數(shù)據(jù)源
*/
public static void switchDb(String dbKey) {
dbKeys.set(dbKey);
}
/**
* 重置數(shù)據(jù)源(切換為默認(rèn)的數(shù)據(jù)源)
*/
public static void resetDb() {
dbKeys.remove();
}
/**
* 獲取當(dāng)前數(shù)據(jù)源的key
*/
public static String currentDb() {
return dbKeys.get();
}
}
DynamicDataSourceConfig
將數(shù)據(jù)源配置到springboot中和初始化Mybaitis配置
package com.dynamicdatadource.dynamic;
import lombok.Data;
import org.apache.ibatis.logging.Log;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
@Configuration
@ConfigurationProperties(prefix = "mybatis")
@Data
public class DynamicDataSourceConfig {
private String mapperLocations;
private String typeAliasesPackage;
@Data
public class MybatisConfiguration{
private String logImpl;
private boolean mapUnderscoreToCamelCase;
}
private MybatisConfiguration configuration=new MybatisConfiguration();
/**
* 動(dòng)態(tài)數(shù)據(jù)源
*/
@Bean
public DynamicDataSource dynamicDataSource() {
DynamicDataSource dataSource = new DynamicDataSource();
Map<Object, Object> targetDataSources = new HashMap<>();
dataSource.setTargetDataSources(targetDataSources);
return dataSource;
}
/**
* 會(huì)話工廠Mybaitis
*/
@Bean
public SqlSessionFactoryBean sqlSessionFactoryBean() throws IOException, ClassNotFoundException {
org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
configuration.setMapUnderscoreToCamelCase(this.configuration.isMapUnderscoreToCamelCase()); //開啟駝峰命名
configuration.setLogImpl((Class<? extends Log>) Class.forName(this.configuration.getLogImpl())); //控制臺(tái)打印sql日志
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dynamicDataSource());
sqlSessionFactoryBean.setConfiguration(configuration);
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
sqlSessionFactoryBean.setMapperLocations(resolver.getResources(mapperLocations));
sqlSessionFactoryBean.setTypeAliasesPackage(typeAliasesPackage);
return sqlSessionFactoryBean;
}
/**
* 事務(wù)管理器
*/
@Bean
public PlatformTransactionManager transactionManager() {
return new DataSourceTransactionManager(dynamicDataSource());
}
}
加載YML數(shù)據(jù)庫(kù)配置類
package com.dynamicdatadource.config;
import com.dynamicdatadource.dynamic.DynamicDataSourceService;
import lombok.Data;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.sql.DataSource;
import java.util.List;
import java.util.Map;
import java.util.Set;
@Component
@Data
@ConfigurationProperties(prefix = "spring.datasource")
public class YmlDataSourceProvider {
private List<Map<String, DataSourceProperties>> multiDb;
private DataSource buildDataSource(DataSourceProperties prop) {
DataSourceBuilder<?> builder = DataSourceBuilder.create();
builder.driverClassName(prop.getDriverClassName());
builder.username(prop.getUsername());
builder.password(prop.getPassword());
builder.url(prop.getUrl());
return builder.build();
}
public void initDataSource() {
multiDb.forEach(map -> {
Set<String> keys = map.keySet();
keys.forEach(key -> {
DataSourceProperties properties = map.get(key);
DataSource dataSource = buildDataSource(properties);
DynamicDataSourceService.addDataSource(key, dataSource);
});
});
}
//在構(gòu)造函數(shù)之后執(zhí)行
@PostConstruct
public void init() {
initDataSource();
}
}aop切換
package com.dynamicdatadource.aop;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.METHOD,ElementType.TYPE})//作用:方法和類
@Retention(RetentionPolicy.RUNTIME)
public @interface DynamicDataSourceAnno {
String key() default "";
}
package com.dynamicdatadource.aop;
import com.dynamicdatadource.dynamic.DynamicDataSourceService;
import org.apache.commons.lang.StringUtils;
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.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
// 用于單獨(dú)的請(qǐng)求或者類進(jìn)行切換數(shù)據(jù)庫(kù)
@Aspect
@Component
public class DynamicDataSourceAspect {
@Pointcut("@annotation(com.dynamicdatadource.aop.DynamicDataSourceAnno)")
public void dynamicDataSourceAnno() {
}
@Around("dynamicDataSourceAnno()")
public Object DynamicDataSourceAspectAroundAnno(ProceedingJoinPoint joinPoint) {
Object object = null;
try {
MethodSignature signature = (MethodSignature)joinPoint.getSignature();
DynamicDataSourceAnno dynamicDataSourceAnno = signature.getMethod().getAnnotation(DynamicDataSourceAnno.class);
String key = dynamicDataSourceAnno.key();
if (StringUtils.isNotBlank(key)) {
//切換為指定數(shù)據(jù)庫(kù)
DynamicDataSourceService.switchDb(key);
}
object = joinPoint.proceed();
} catch (Throwable e) {
e.printStackTrace();
}finally {
//還原為默認(rèn)配置
DynamicDataSourceService.resetDb();
}
return object;
}
// 還可以擴(kuò)展包路徑切換
}
效果
運(yùn)行程序之后,就會(huì)將數(shù)據(jù)源加入到數(shù)據(jù)源列表中了

擴(kuò)展
MysqlDataSourceInitialize
從數(shù)據(jù)庫(kù)中將配置信息查詢出來,然后動(dòng)態(tài)添加到數(shù)據(jù)源列表中
package com.dao.config;
import com.dao.DatasourceDao;
import com.dynamicdatadource.aop.DynamicDataSourceAnno;
import com.dynamicdatadource.dynamic.DynamicDataSourceService;
import com.entity.DataSourceEneity;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.sql.DataSource;
import java.util.List;
//從數(shù)據(jù)庫(kù)中查詢出全部的數(shù)據(jù)源,添加到數(shù)據(jù)源容器中
/**
* 表結(jié)構(gòu)如下:
*
* CREATE TABLE `t_datasource` (
* `id` int(11) NOT NULL,
* `key` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '綁定的key,用于數(shù)據(jù)源的切換',
* `url` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '數(shù)據(jù)庫(kù)連接地址',
* `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '數(shù)據(jù)庫(kù)用戶名',
* `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '數(shù)據(jù)庫(kù)密碼',
* `driverClassName` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '數(shù)據(jù)庫(kù)驅(qū)動(dòng)',
* `type` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '數(shù)據(jù)庫(kù)類型: mysql ,oracle,..',
* `state` int(2) NOT NULL COMMENT '是否可用: 1可用 ,2不可用',
* PRIMARY KEY (`id`),
* UNIQUE KEY `key` (`key`)
* ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
*
* 上表要放入到默認(rèn)數(shù)據(jù)源中的數(shù)據(jù)庫(kù)里才行
*/
@Component
public class MysqlDataSourceInitialize implements ApplicationRunner {
@Autowired
private DatasourceDao datasourceDao;
//項(xiàng)目啟動(dòng)后執(zhí)行初始化數(shù)據(jù)源
@Override
public void run(ApplicationArguments args) throws Exception {
try {
List<DataSourceEneity> dataSources = datasourceDao.getDataSources();
for (DataSourceEneity dataSource : dataSources) {
DynamicDataSourceService.addDataSource(dataSource.getKey(),dataSource.getDataSource());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
DataSourceEneity實(shí)體類
@Data
public class DataSourceEneity {
private int id;
private String key;
private String url;
private String username;
private String password;
private String driverClassName;
private String type;
private int state;
public DataSource getDataSource() {
DataSourceBuilder<?> builder = DataSourceBuilder.create();
builder.driverClassName(driverClassName);
builder.username(username);
builder.password(password);
builder.url(url);
return builder.build();
}
}總結(jié)
到此這篇關(guān)于SpringBoot多數(shù)據(jù)源切換實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)SpringBoot多數(shù)據(jù)源切換內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- 在SpringBoot項(xiàng)目中動(dòng)態(tài)切換數(shù)據(jù)源和數(shù)據(jù)庫(kù)的詳細(xì)步驟
- SpringBoot實(shí)現(xiàn)數(shù)據(jù)源動(dòng)態(tài)切換的最佳姿勢(shì)
- SpringBoot項(xiàng)目中如何動(dòng)態(tài)切換數(shù)據(jù)源、數(shù)據(jù)庫(kù)
- SpringBoot實(shí)現(xiàn)動(dòng)態(tài)數(shù)據(jù)源切換的項(xiàng)目實(shí)踐
- SpringBoot實(shí)現(xiàn)動(dòng)態(tài)數(shù)據(jù)源切換的方法總結(jié)
- 使用SpringBoot動(dòng)態(tài)切換數(shù)據(jù)源的實(shí)現(xiàn)方式
- Springboot實(shí)現(xiàn)多數(shù)據(jù)源切換詳情
- SpringBoot實(shí)現(xiàn)動(dòng)態(tài)切換數(shù)據(jù)源的示例代碼
相關(guān)文章
Java輕松掌握面向?qū)ο蟮娜筇匦苑庋b與繼承和多態(tài)
本文主要講述的是面向?qū)ο蟮娜筇匦裕悍庋b,繼承,多態(tài),內(nèi)容含括從封裝到繼承再到多態(tài)的所有重點(diǎn)內(nèi)容以及使用細(xì)節(jié)和注意事項(xiàng),內(nèi)容有點(diǎn)長(zhǎng),請(qǐng)大家耐心看完2022-05-05
springboot?publish?event?事件機(jī)制demo分享
這篇文章主要介紹了springboot?publish?event?事件機(jī)制demo,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-10-10
Java如何使用正則表達(dá)式從字符串中提取數(shù)字
這篇文章主要介紹了Java如何使用正則表達(dá)式從字符串中提取數(shù)字問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-12-12
Java數(shù)據(jù)結(jié)構(gòu)二叉樹難點(diǎn)解析
樹是一種重要的非線性數(shù)據(jù)結(jié)構(gòu),直觀地看,它是數(shù)據(jù)元素(在樹中稱為結(jié)點(diǎn))按分支關(guān)系組織起來的結(jié)構(gòu),很象自然界中的樹那樣。樹結(jié)構(gòu)在客觀世界中廣泛存在,如人類社會(huì)的族譜和各種社會(huì)組織機(jī)構(gòu)都可用樹形象表示2021-10-10
Springboot公共字段填充及ThreadLocal模塊改進(jìn)方案
這篇文章主要為大家介紹了Springboot公共字段填充及ThreadLocal模塊改進(jìn)方案詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-11-11

