书接上文使用nacos配置多数据源(druid)AbstractRoutingDataSource
根据配置文件切换数据源讲了,但对于某些特殊场景可能依旧有点问题,比如通过数据源生成代码,导出文档什么的,可能会有多个数据源,而且这些数据源肯定是不会走nacos的所以就有了本篇文章,写的很糙请谅解,可以自己优化
修改了多数据源bean的配置类
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.db.Db;
import com.alibaba.druid.pool.DruidDataSource;
import lombok.SneakyThrows;
import org.apache.commons.lang.StringUtils;
import org.jasypt.encryption.StringEncryptor;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import javax.sql.DataSource;
import java.util.*;
import java.util.stream.Collectors;
import static com.joolun.cloud.salaryother.api.config.DynamicDataSourceSwitcher.MATER;
import static com.joolun.cloud.salaryother.api.config.DynamicDataSourceSwitcher.SLAVE1;
/**
* 多数据源bean的配置类
*
* @author ycg
*/
@Configuration
public class MultipleDataSourceConfig {
// private static final Log logger = LogFactory.getLog(JdbcUtils.class);
private final StringEncryptor stringEncryptor;
public MultipleDataSourceConfig(StringEncryptor stringEncryptor) {
this.stringEncryptor = stringEncryptor;
}
@Bean(MATER)
@ConfigurationProperties(prefix = "spring.datasource.master")
public DataSource createMasterDataSource() {
return new DruidDataSource();
}
@Bean(SLAVE1)
@ConfigurationProperties(prefix = "spring.datasource.slave1")
public DataSource createSlave1DataSource() {
return new DruidDataSource();
}
/**
* 设置动态数据源,通过@Primary 来确定主DataSource
*
* @return
*/
@Bean
@Primary
@SneakyThrows
public DataSource createDynamicDataSource(@Qualifier(MATER) DataSource master, @Qualifier(SLAVE1) DataSource slave1) {
DynamicDataSource dynamicDataSource = new DynamicDataSource();
//设置默认数据源
dynamicDataSource.setDefaultTargetDataSource(master);
//配置多数据源
Map<Object, Object> druidDataSourceMap = new HashMap<>(16);
try {
druidDataSourceMap = Db.use(master)
.query("select id,name,url,username,password,driver_class_name from datasource_data where del_flag = 0", DatasourceData.class).parallelStream()
.filter(ObjectUtil::isNotNull).peek(n -> n.setPassword(stringEncryptor.decrypt(n.getPassword())))
.map(n -> {
DruidDataSource druidDataSource = new DruidDataSource();
BeanUtils.copyProperties(master, druidDataSource);
if (StringUtils.isNotBlank(n.getDriverClassName())) {
druidDataSource.setDriverClassName(n.getDriverClassName());
}
druidDataSource.setName(n.getName());
druidDataSource.setUrl(n.getUrl());
druidDataSource.setUsername(n.getUsername());
druidDataSource.setPassword(n.getPassword());
return druidDataSource;
}).collect(Collectors.toMap(DruidDataSource::getName, n -> n, (v1, v2) -> v2));
} finally {
druidDataSourceMap.put("master", master);
druidDataSourceMap.put("slave1", slave1);
}
dynamicDataSource.setTargetDataSources(druidDataSourceMap);
return dynamicDataSource;
}
}
修改
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import static com.joolun.cloud.salaryother.api.config.DynamicDataSourceSwitcher.MATER;
/**
* 多数据源
*
* @author xiyu
*/
public class DynamicDataSource extends AbstractRoutingDataSource {
Logger logger = LoggerFactory.getLogger(DynamicDataSource.class);
@Override
protected Object determineCurrentLookupKey() {
String dataSource = DynamicDataSourceSwitcher.getDataSource();
if (StringUtils.isBlank(dataSource)) return MATER;
if (getResolvedDataSources().containsKey(dataSource)) {
return dataSource;
}
logger.info("数据源中不包含切换源----------使用默认数据源 {}", MATER);
return MATER;
}
}
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
/**
* 创建aop切面
*
* @author xiyu
*/
@Aspect
@Component
@Order(1)
public class DynamicDataSourceAspect {
/**
* 切入点只对@Service注解的类上的@DataSource方法生效
*
* @param myDataSource
*/
@Pointcut(value = "@within(org.springframework.stereotype.Service) && @annotation(myDataSource)")
public void dynamicDataSourcePointCut(MyDataSource myDataSource) {
}
@Before(value = "dynamicDataSourcePointCut(myDataSource)", argNames = "myDataSource")
public void switchDataSource(MyDataSource myDataSource) {
DynamicDataSourceSwitcher.setDataSource(myDataSource.value());
}
/**
* 切点执行完后 切换成主数据库
*
* @param myDataSource
*/
@After(value = "dynamicDataSourcePointCut(myDataSource)", argNames = "myDataSource")
public void after(MyDataSource myDataSource) {
DynamicDataSourceSwitcher.cleanDataSource();
}
}
/**
* 操作数据源
*
* @author xiyu
*/
public class DynamicDataSourceSwitcher {
public static final String MATER = "master";
public static final String SLAVE1 = "slave1";
private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();
public static void setDataSource(String name) {
CONTEXT_HOLDER.set(name);
}
public static String getDataSource() {
return CONTEXT_HOLDER.get();
}
public static void cleanDataSource() {
CONTEXT_HOLDER.remove();
}
}
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import static com.joolun.cloud.salaryother.api.config.DynamicDataSourceSwitcher.MATER;
/**
* 自定义的数据源的注解
*
* @author xiyu
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({
ElementType.METHOD
})
public @interface MyDataSource {
String value() default MATER;
}
以下都是老代码懒得改了
增加对数据库操作 service层
private final StringEncryptor stringEncryptor;
@Qualifier("createDynamicDataSource")
private DynamicDataSource dataSource;
/**
* 保存数据源并且加密
*
* @param datasourceData
* @return
*/
@Override
public Boolean saveSysDatasource(DatasourceData datasourceData) {
//校验数据源配置是否能链接
try {
DriverManager.getConnection(datasourceData.getUrl(), datasourceData.getUsername(), datasourceData.getPassword());
}catch (SQLException e){
log.error(e.getMessage(), e);
throw new RuntimeException("连接失败,请检查配置信息:" + e.getMessage());
}
//添加动态数据源
// DynamicDataSource dynamicDataSource = new DynamicDataSource();
Map<Object, Object> map = new HashMap<>(16);
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setName(datasourceData.getName());
druidDataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
if(StringUtils.isNotBlank(datasourceData.getDriverClassName())){
druidDataSource.setDriverClassName(datasourceData.getDriverClassName());
}
druidDataSource.setUrl(datasourceData.getUrl());
druidDataSource.setUsername(datasourceData.getUsername());
druidDataSource.setPassword(datasourceData.getPassword());
map.put(datasourceData.getName(), druidDataSource);
dataSource.setTargetDataSources(map);
dataSource.afterPropertiesSet();
datasourceData.setPassword(stringEncryptor.encrypt(datasourceData.getPassword()));
datasourceData.setTenantId(null);
this.baseMapper.insert(datasourceData);
return Boolean.TRUE;
}
/**
* 更新数据源
*
* @param datasourceData
* @return
*/
@Override
public Boolean updateSysDatasource(DatasourceData datasourceData) {
//校验数据源配置是否能链接
try {
DriverManager.getConnection(datasourceData.getUrl(),datasourceData.getUsername(),datasourceData.getPassword());
}catch (SQLException e){
log.error(e.getMessage(), e);
throw new RuntimeException("连接失败,请检查配置信息:" + e.getMessage());
}
//先删除动态数据源
DynamicDataSourceSwitcher.cleanDataSource();
Map<Object, DataSource> resolvedDataSources = dataSource.getResolvedDataSources();
if(resolvedDataSources.containsKey(datasourceData.getName())){
resolvedDataSources.remove(datasourceData.getName());
}
//再添加动态数据源
// DynamicDataSource dynamicDataSource = new DynamicDataSource();
Map<Object, Object> map = new HashMap<>(16);
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setName(datasourceData.getName());
druidDataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
if(StringUtils.isNotBlank(datasourceData.getDriverClassName())){
druidDataSource.setDriverClassName(datasourceData.getDriverClassName());
}
druidDataSource.setUrl(datasourceData.getUrl());
druidDataSource.setUsername(datasourceData.getUsername());
druidDataSource.setPassword(datasourceData.getPassword());
map.put(datasourceData.getName(), druidDataSource);
dataSource.setTargetDataSources(map);
dataSource.afterPropertiesSet();
if (StringUtils.isNotBlank(datasourceData.getPassword())) {
datasourceData.setPassword(stringEncryptor.encrypt(datasourceData.getPassword()));
}
this.baseMapper.updateById(datasourceData);
return Boolean.TRUE;
}
@Data
@ApiModel(value="数据源表")
@TableName("datasource_data")
@EqualsAndHashCode(callSuper = true)
public class DatasourceData extends Model<DatasourceData> {
private static final long serialVersionUID = 1L;
/**
* 主键
*/
@ApiModelProperty(value = "主键")
@TableId(type = IdType.ASSIGN_ID)
private String id;
/**
* 名称
*/
@ApiModelProperty(value = "名称")
@NotBlank(message = "数据源名称不能为空")
private String name;
/**
* jdbcurl
*/
@ApiModelProperty(value = "jdbcurl")
@NotBlank(message = "jdbcurl不能为空")
private String url;
/**
* 用户名
*/
@ApiModelProperty(value = "用户名")
@NotBlank(message = "用户名不能为空")
private String username;
/**
* 密码
*/
@ApiModelProperty(value = "密码")
@NotBlank(message = "密码不能为空")
private String password;
/**
* 驱动
*/
@ApiModelProperty(value = "驱动")
@NotBlank(message = "驱动不能为空")
private String driverClassName;
/**
* 创建时间
*/
@ApiModelProperty(value = "创建时间")
private LocalDateTime createTime;
/**
* 更新时间
*/
@ApiModelProperty(value = "更新时间")
private LocalDateTime updateTime;
/**
* 删除标记
*/
@ApiModelProperty(value = "删除标记")
private String delFlag;
/**
* 租户ID
*/
@ApiModelProperty(value = "租户ID")
private String tenantId;
}
获取数据库表名列表和表字段
<select id="queryList" resultType="map">
select table_name tableName, engine, table_comment tableComment, create_time createTime from information_schema.tables
where table_schema = (select database())
<if test="tableName != null and tableName.trim() != ''">
and table_name like concat('%', #{tableName}, '%')
</if>
order by create_time desc
</select>
<select id="queryTable" resultType="map">
select table_name tableName, engine, table_comment tableComment, create_time createTime from information_schema.tables
where table_schema = (select database()) and table_name = #{tableName}
</select>
<select id="queryColumns" resultType="map">
select column_name columnName, data_type dataType, column_comment columnComment, column_key columnKey, extra, character_maximum_length, is_nullable from information_schema.columns
where table_name = #{tableName} and table_schema = (select database()) order by ordinal_position
</select>