mybatis-plus動(dòng)態(tài)數(shù)據(jù)源讀寫分離方式
1、背景
在實(shí)際項(xiàng)目的開發(fā)過程中,一定會(huì)存在主庫與從庫的分布式模式,主庫進(jìn)行增刪改,從庫進(jìn)行查詢。
這樣可以保證對(duì)不同的數(shù)據(jù)庫進(jìn)行操作,減少對(duì)數(shù)據(jù)庫的壓力。
2、動(dòng)態(tài)數(shù)據(jù)源
創(chuàng)建DynamicDatasourceService
import com.jiuqi.grid.collection.entity.datasource.DataSourceDTO; import java.util.Set; public interface DynamicDatasourceService { /** * 獲取所有數(shù)據(jù)源 * * @return */ Set<String> datasources(); /** * 添加數(shù)據(jù)源 * * @param dto 數(shù)據(jù)源信息 * @return */ Set<String> add(DataSourceDTO dto); /** * 移除數(shù)據(jù)源 * * @param name 連接池名稱 */ void remove(String name); }
實(shí)現(xiàn)DynamicDatasourceService
package com.jiuqi.grid.collection.service.impl; import com.baomidou.dynamic.datasource.DynamicRoutingDataSource; import com.baomidou.dynamic.datasource.creator.DefaultDataSourceCreator; import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DataSourceProperty; import com.jiuqi.grid.collection.entity.datasource.DataSourceDTO; import com.jiuqi.grid.collection.service.DynamicDatasourceService; import org.springframework.beans.BeanUtils; import org.springframework.stereotype.Service; import javax.sql.DataSource; import java.util.Set; @Service public class DynamicDatasourceServiceImpl implements DynamicDatasourceService { private final DataSource dataSource; private final DefaultDataSourceCreator dataSourceCreator; public DynamicDatasourceServiceImpl( DataSource dataSource, DefaultDataSourceCreator dataSourceCreator) { this.dataSource = dataSource; this.dataSourceCreator = dataSourceCreator; } /** * 獲取所有數(shù)據(jù)源 * * @return */ @Override public Set<String> datasources() { DynamicRoutingDataSource ds = (DynamicRoutingDataSource) dataSource; return ds.getDataSources().keySet(); } /** * 添加數(shù)據(jù)源 * * @param dto * @return */ @Override public Set<String> add(DataSourceDTO dto) { DataSourceProperty dataSourceProperty = new DataSourceProperty(); BeanUtils.copyProperties(dto, dataSourceProperty); DynamicRoutingDataSource ds = (DynamicRoutingDataSource) dataSource; DataSource dataSource = dataSourceCreator.createDataSource(dataSourceProperty); ds.addDataSource(dto.getPollName(), dataSource); return ds.getDataSources().keySet(); } /** * 刪除數(shù)據(jù)源 * * @param name */ @Override public void remove(String name) { DynamicRoutingDataSource ds = (DynamicRoutingDataSource) dataSource; ds.removeDataSource(name); } }
2.1添加數(shù)據(jù)源
我這里是讀取的配置文件里的用戶名、密碼等數(shù)據(jù)源的鏈接
public void addDatasource(String tenant) { if (dynamicDatasourceService.datasources().contains(tenant)) { return; } DataSourceDTO datasourceDTO = new DataSourceDTO(); //庫名 datasourceDTO.setPollName(tenant); //鏈接信息 讀取配置文件的主庫信息 datasourceDTO.setDriverClassName(sqlDataSource.getDriverClassName()); //替換數(shù)據(jù)庫 if (StringUtils.isEmpty(sqlDataSource.getUrl())) { throw new RuntimeException("配置文件讀取失敗"); } String url = sqlDataSource.getUrl(); url = url.replace("grid_test", tenant); datasourceDTO.setUrl(url); datasourceDTO.setUsername(sqlDataSource.getUsername()); datasourceDTO.setPassword(sqlDataSource.getPassword()); dynamicDatasourceService.add(datasourceDTO); }
2.2數(shù)據(jù)源
調(diào)用上面的addDatasource()方法,數(shù)據(jù)源就會(huì)創(chuàng)建成功,這里要注意的是,每一次增刪改查都需要調(diào)用此方法,以防數(shù)據(jù)源不存在。
3、分庫查詢
創(chuàng)建新的數(shù)據(jù)源目的就是為了可以進(jìn)行分庫操作。
分庫操作步驟如下:
3.1添加配置類
package com.jiuqi.grid.collection.dsprocessor; import com.baomidou.dynamic.datasource.processor.DsHeaderProcessor; import com.baomidou.dynamic.datasource.processor.DsProcessor; import com.baomidou.dynamic.datasource.processor.DsSessionProcessor; import com.baomidou.dynamic.datasource.processor.DsSpelExpressionProcessor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * 注冊(cè)動(dòng)態(tài)數(shù)據(jù)源解析器 */ @Configuration public class MyDynamicDataSourceConfig { @Bean public DsProcessor dsProcessor() { DsMapProcessor mapProcessor = new DsMapProcessor(); DsHeaderProcessor headerProcessor = new DsHeaderProcessor(); DsSessionProcessor sessionProcessor = new DsSessionProcessor(); DsSpelExpressionProcessor spelExpressionProcessor = new DsSpelExpressionProcessor(); mapProcessor.setNextProcessor(headerProcessor); headerProcessor.setNextProcessor(sessionProcessor); sessionProcessor.setNextProcessor(spelExpressionProcessor); return mapProcessor; } }
package com.jiuqi.grid.collection.dsprocessor; import com.baomidou.dynamic.datasource.processor.DsProcessor; import java.util.Map; import com.jiuqi.grid.collection.consts.SQLConstants; import com.jiuqi.grid.collection.dto.common.Address3DTO; import com.jiuqi.grid.collection.dto.data.CollectionDataPageParam; import com.jiuqi.grid.collection.entity.CollectionDataDO; import org.aopalliance.intercept.MethodInvocation; /** * 通過params map中獲取數(shù)據(jù)源標(biāo)識(shí) * * @author songlude */ public class DsMapProcessor extends DsProcessor { /** * 抽象匹配條件 匹配才會(huì)走當(dāng)前執(zhí)行器否則走下一級(jí)執(zhí)行器 * * @param key DS注解里的內(nèi)容 * @return 是否匹配 */ @Override public boolean matches(String key) { return key.equals("#" + SQLConstants.EXEC_DATASOURCE); } /** * 抽象最終決定數(shù)據(jù)源 * * @param invocation 方法執(zhí)行信息 * @param key DS注解里的內(nèi)容 * @return 數(shù)據(jù)源名稱 */ @Override public String doDetermineDatasource(MethodInvocation invocation, String key) { Object[] arguments = invocation.getArguments(); Object argument = arguments[0]; if (argument instanceof Map) { Map<String, Object> params = (Map<String, Object>) argument; Object value = params.get(SQLConstants.EXEC_DATASOURCE); return value == null ? null : value.toString(); } else if (argument instanceof CollectionDataPageParam) { CollectionDataPageParam collectionDataPageParam = (CollectionDataPageParam) argument; return collectionDataPageParam.getGRID_DATASOURCE(); } else if (argument instanceof CollectionDataDO) { CollectionDataDO collectionDataDO = (CollectionDataDO) argument; return collectionDataDO.getGRID_DATASOURCE(); } else if (argument instanceof Address3DTO) { Address3DTO address3DTO = (Address3DTO) argument; return address3DTO.getGRID_DATASOURCE(); } return null; } }
這里要注意需要把“argument”進(jìn)行類型的判斷,argument 就是你查詢Mapper的參數(shù),這里我獲取的是argument[0],則Mapper參數(shù)的位置在第一個(gè)。
3.2 添加數(shù)據(jù)源查詢
通過@DS注解來指定數(shù)據(jù)源,在配置類中指定了以#開頭
@DS("#GRID_DATASOURCE") @UpdateProvider(type = QueryProvider.class, method = "createDatabase") void createDatabase(Map<String, Object> params);
這樣就可以開啟你的動(dòng)態(tài)數(shù)據(jù)源,分庫查詢啦。
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Springboot?hibernate-validator?6.x快速校驗(yàn)示例代碼
這篇文章主要介紹了Springboot?hibernate-validator?6.x校驗(yàn),本文以6.2.1.Final版本為例解決了log4j版本的漏洞問題,通過實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2021-12-12一文徹底弄懂零拷貝原理以及java實(shí)現(xiàn)
零拷貝(英語: Zero-copy) 技術(shù)是指計(jì)算機(jī)執(zhí)行操作時(shí),CPU不需要先將數(shù)據(jù)從某處內(nèi)存復(fù)制到另一個(gè)特定區(qū)域,下面這篇文章主要給大家介紹了關(guān)于零拷貝原理以及java實(shí)現(xiàn)的相關(guān)資料,需要的朋友可以參考下2021-08-08Java中DataInputStream和DataOutputStream的使用方法
這篇文章主要介紹了Java中DataInputStream和DataOutputStream的使用方法,通過創(chuàng)建對(duì)象展開具體的內(nèi)容介紹,需要的小伙伴可以參考一下2022-05-05詳解Java的Hibernate框架中的緩存與二級(jí)緩存
這篇文章主要介紹了Java的Hibernate框架中的緩存與二級(jí)緩存,Hibernate是Java的SSH三大web開發(fā)框架之一,需要的朋友可以參考下2015-12-12IntelliJ IDEA Run時(shí)報(bào)“無效的源發(fā)行版:16“錯(cuò)誤問題及解決方法
這篇文章主要介紹了IntelliJ IDEA Run時(shí)報(bào)“無效的源發(fā)行版:16“錯(cuò)誤問題及解決方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-05-05解決IDEA中maven導(dǎo)入jar包一直報(bào)錯(cuò)問題
這篇文章主要介紹了解決IDEA中maven導(dǎo)入jar包一直報(bào)錯(cuò)問題,本文通過實(shí)例圖文的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-04-04java如何實(shí)現(xiàn)postman中用x-www-form-urlencoded參數(shù)的請(qǐng)求
在Java開發(fā)中,模擬Postman發(fā)送x-www-form-urlencoded類型的請(qǐng)求是一個(gè)常見需求,本文主要介紹了如何在Java中實(shí)現(xiàn)這一功能,首先,需要通過導(dǎo)入http-client包來創(chuàng)建HTTP客戶端,接著,利用該客戶端發(fā)送Post請(qǐng)求2024-09-09SpringBoot最新定時(shí)任務(wù)的7種實(shí)現(xiàn)方案
在現(xiàn)代應(yīng)用中,定時(shí)任務(wù)是一個(gè)非常常見的需求,本文將通過7種方式講解如何在SpringBoot中實(shí)現(xiàn)定時(shí)任務(wù),包括使用@Scheduled注解、ScheduledExecutorService、Quartz、SpringTaskScheduler、Redis、XXL-JOB和Elastic-Job等,各有優(yōu)缺點(diǎn),選擇時(shí)應(yīng)根據(jù)實(shí)際需求進(jìn)行考慮2024-12-12