mybatis-plus动态数据源读写分离方式
作者:杨村你喜哥l
在分布式项目开发中,动态数据源的配置与使用至关重要,通过创建DynamicDatasourceService,实现数据源的动态添加与调用,有效管理主从库操作,减轻数据库压力,此外,通过配置类与@DS注解,实现了灵活的分库查询功能,为高效处理数据提供了强有力的支持
1、背景
在实际项目的开发过程中,一定会存在主库与从库的分布式模式,主库进行增删改,从库进行查询。
这样可以保证对不同的数据库进行操作,减少对数据库的压力。
2、动态数据源
创建DynamicDatasourceService
import com.jiuqi.grid.collection.entity.datasource.DataSourceDTO; import java.util.Set; public interface DynamicDatasourceService { /** * 获取所有数据源 * * @return */ Set<String> datasources(); /** * 添加数据源 * * @param dto 数据源信息 * @return */ Set<String> add(DataSourceDTO dto); /** * 移除数据源 * * @param name 连接池名称 */ void remove(String name); }
实现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; } /** * 获取所有数据源 * * @return */ @Override public Set<String> datasources() { DynamicRoutingDataSource ds = (DynamicRoutingDataSource) dataSource; return ds.getDataSources().keySet(); } /** * 添加数据源 * * @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(); } /** * 删除数据源 * * @param name */ @Override public void remove(String name) { DynamicRoutingDataSource ds = (DynamicRoutingDataSource) dataSource; ds.removeDataSource(name); } }
2.1添加数据源
我这里是读取的配置文件里的用户名、密码等数据源的链接
public void addDatasource(String tenant) { if (dynamicDatasourceService.datasources().contains(tenant)) { return; } DataSourceDTO datasourceDTO = new DataSourceDTO(); //库名 datasourceDTO.setPollName(tenant); //链接信息 读取配置文件的主库信息 datasourceDTO.setDriverClassName(sqlDataSource.getDriverClassName()); //替换数据库 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数据源
调用上面的addDatasource()方法,数据源就会创建成功,这里要注意的是,每一次增删改查都需要调用此方法,以防数据源不存在。
3、分库查询
创建新的数据源目的就是为了可以进行分库操作。
分库操作步骤如下:
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; /** * 注册动态数据源解析器 */ @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中获取数据源标识 * * @author songlude */ public class DsMapProcessor extends DsProcessor { /** * 抽象匹配条件 匹配才会走当前执行器否则走下一级执行器 * * @param key DS注解里的内容 * @return 是否匹配 */ @Override public boolean matches(String key) { return key.equals("#" + SQLConstants.EXEC_DATASOURCE); } /** * 抽象最终决定数据源 * * @param invocation 方法执行信息 * @param key DS注解里的内容 * @return 数据源名称 */ @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”进行类型的判断,argument 就是你查询Mapper的参数,这里我获取的是argument[0],则Mapper参数的位置在第一个。
3.2 添加数据源查询
通过@DS注解来指定数据源,在配置类中指定了以#开头
@DS("#GRID_DATASOURCE") @UpdateProvider(type = QueryProvider.class, method = "createDatabase") void createDatabase(Map<String, Object> params);
这样就可以开启你的动态数据源,分库查询啦。
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。