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);
这样就可以开启你的动态数据源,分库查询啦。欢迎留言