1、废话不多说,首先引入依赖(备注:注意这里的依赖,版本不同会出现让人头炸的BUG,所以配置依赖是关键,网上搜索都是半句话压根没结果性,所以要注意)
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>3.4.1</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.3.4</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.4</version>
</dependency>
2、配置主数据源(这里只需配置主数据源)
spring:
datasource:
dynamic:
primary: master #这里是本地主库,主要用来存储第三方数据源配置信息表及用户表的选择
strict: false
datasource:
master:
url: jdbc:mysql://localhost:3306/test?useSSL=false&useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&zeroDateTimeBehavior=convertToNull&serverTimezone=UTC&serverTimezone=GMT%2B8
username: root
password: 123456
driverClassName: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
druid:
maxActive: 300
initialSize: 20
maxWait: 6000
minIdle: 20
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 30000
validationQuery: select 'x'
testWhileIdle: true
testOnBorrow: true
testOnReturn: false
3、配置公共数据源切换类(备注我这里的子库链接信息是存储在主库表内,流程-》主库内获取数据配置信息表-》写入到DataSourceProperty的对象内,然后加入到核心数据源组件内)
package com.kk.datamap.masterdata.common.dbconfig;
import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
import com.baomidou.dynamic.datasource.creator.*;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DataSourceProperty;
import com.kk.datamap.masterdata.common.constant.SystemConstants;
import com.kk.datamap.masterdata.po.CdcDatasourceConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.sql.DataSource;
import java.util.Set;
/**
* @author: kk
* @Email: [email protected]
* @Description: 公共动态数据源配置类
* @date: 2022/1/14 5:25 下午
* @Version V1.0
*/
@Service
public class DynamicDataSourceConfig {
@Autowired
DataSource dataSource;
@Autowired
DefaultDataSourceCreator dataSourceCreator;
/**
* 获取当前数据源
*
* @return
*/
public Set<String> dataSourceNow() {
DynamicRoutingDataSource ds = (DynamicRoutingDataSource) dataSource;
return ds.getDataSources().keySet();
}
/**
* 添加数据源
*
* @param dto
* @return
*/
public Set<String> add(DatasourceConfig dto) {
DataSourceProperty dataSourceProperty = new DataSourceProperty();
dataSourceProperty.setDriverClassName(dto.getDriverClassName());
dataSourceProperty.setUrl(dto.getDatabaseIp());
dataSourceProperty.setUsername(dto.getDatabaseAccount());
dataSourceProperty.setPassword(dto.getDatabasePassword());
DynamicRoutingDataSource ds = (DynamicRoutingDataSource) dataSource;
DataSource dataSource = dataSourceCreator.createDataSource(dataSourceProperty);
ds.addDataSource(dto.getDatasourceType(), dataSource);//这里写入ds注解的时候建议制定同种类型数据库走一个ds注解值(我这里实际需求会从数据库获取自定义的ds注解值)
return ds.getDataSources().keySet();
}
/**
* 删除数据源
*
* @param name
* @return
*/
public String remove(String name) {
DynamicRoutingDataSource ds = (DynamicRoutingDataSource) dataSource;
ds.removeDataSource(name);
return name + "删除成功!";
}
}
5、定义实体类对象
package com.kk.datamap.masterdata.po;
import com.alibaba.druid.support.monitor.annotation.MTable;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import java.io.Serializable;
import java.util.Date;
import lombok.Data;
/**
* datasource_config
* @author
*/
@ApiModel(value="数据源配置表")
@Data
public class DataSourceConfig implements Serializable {
private Integer id;
/**
* 数据源ID
*/
@ApiModelProperty(value="数据源ID")
private Integer datasourceId;
/**
* 主数据ID
*/
@ApiModelProperty(value="主数据ID")
private Integer mdmModelId;
/**
* 数据源类型
*/
@ApiModelProperty(value="数据源类型")
private String datasourceType;
/**
* 数据库IP
*/
@ApiModelProperty(value="数据库IP")
private String databaseIp;
/**
* 数据库端口
*/
@ApiModelProperty(value="数据库端口")
private String databasePort;
/**
* 数据库名
*/
@ApiModelProperty(value="数据库名")
private String databaseName;
/**
* 数据库账户名
*/
@ApiModelProperty(value="数据库账户名")
private String databaseAccount;
/**
* 数据库密码
*/
@ApiModelProperty(value="数据库密码")
private String databasePassword;
/**
* 创建人
*/
@ApiModelProperty(value="创建人")
private String creator;
/**
* 创建时间
*/
@ApiModelProperty(value="创建时间")
private Date createTime;
/**
* 修改人
*/
@ApiModelProperty(value="修改人")
private String updator;
/**
* 修改时间
*/
@ApiModelProperty(value="修改时间")
private Date updateTime;
/**
* 数据源驱动名
*/
@ApiModelProperty("数据源驱动名")
private String driverClassName;
private static final long serialVersionUID = 1L;
}
6、定义子库mapper(备注:这里主库可以随意创建mapper,子库只需增加@DS(”“)注解)
package com.kk.datamap.masterdata.dao.datatype;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.kk.datamap.masterdata.common.constant.SystemConstants;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
import java.util.Map;
/**
* @author: kk
* @Email: [email protected]
* @Description:
* @date: 2022/1/13 4:53 下午
* @Version V1.0
*/
@Mapper
public interface MySqlMapper {
//备注:这里我定义的是一个公共DS组件,但是我们实际需求是支持多种不同数据库(并且可能会有两个或者多个同种类型数据库)所以这种情况下建议相同数据库类型可以制定同一个注解(例如:MySQL可以制定@DS(“mysql”),PG可以制定为@DS(”pg”)等)其次注意建议要放置在不同mapper或者不同方法上
@DS(“mysql”)
List<Map<String, Object>> selectByMySqlDataSourceId(@Param("fields") String fields, @Param("tableName") String tableName);
@DS(“pg")
List<Map<String, Object>> selectByPgDataSourceId(@Param("fields") String fields, @Param("tableName") String tableName);
@DS(“oracle")
List<Map<String, Object>> selectByOracleDataSourceId(@Param("fields") String fields, @Param("tableName") String tableName);
}
6、定义xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.gennlife.datamap.masterdata.dao.datatype.CDCMySqlMapper">
<select id="selectByDataSourceId" resultType="java.util.LinkedHashMap">
SELECT ${fields}
FROM ${tableName}
</select>
</mapper>
7、单元测试
@Autowired
DynamicDataSourceConfig dynamicDataSourceConfig;
@Autowired
MySqlMapper mySqlMapper;
@Autowired
DatasourceConfigMapper datasourceConfigMapper;
@Test
public void Test(){
//数据库切换(注:我这里只会增加子数据源,主数据源不会影响,删除的时候也是删除子数据源)
DatasourceConfig datasourceConfig = datasourceConfigMapper.selectByModelId(requestBody.getModelId());
Set<String> addDataSource = dynamicDataSourceConfig.add(datasourceConfig);
log.info("当前已增加数据源为:" + JSON.toJSON(addDataSource));
List<Map<String, Object>> list = mySqlMapper.selectByDataSourceId(StringUtils.strip(fieldList.toString(), "[]"), modelInfo.getModelName());
String dataSourceName = dynamicDataSourceConfig.remove(datasourceConfig.getDatasourceType());
log.info("当前已删除数据源为:" + dataSourceName);
//拼接数据库类型mapper
System.out.println(JSON.toJSON(list));
}
总结结果(这里只是大致使用流程,如果后期接触数据场景,建议深入研究下dynamicDataSource底层源码,其次DS注解也可以动态后面可以优化下,有新的想法欢迎一起留言沟通):