fix: 优化了动态数据源配置

This commit is contained in:
tangwei 2026-09-08 11:28:04 +08:00
parent d4992b9687
commit f0844de9ec
8 changed files with 665 additions and 170 deletions

View File

@ -2,21 +2,31 @@ package com.yfd.platform.datasource;
/** /**
* 数据源 key 常量 * 数据源 key 常量
* <p>
* 系统内置 keyyml 固定配置
* <ul>
* <li>{@link #DM_MASTER} / {@link #DM_SLAVE} 达梦数据库</li>
* </ul>
* 扩展数据源 key 格式{@code ext-{type}-{序号}} {@code ext-oracle-01}{@code ext-mysql-01}
* 由数据库表 {@code GEN_DATASOURCE_CONF.DS_KEY} 配置运行时动态注册
*/ */
public interface DataSourceKeys { public interface DataSourceKeys {
/** 达梦主库 */ /** 达梦主库业务主库yml 固定配置) */
String DM_MASTER = "dm-master"; String DM_MASTER = "dm-master";
/** 达梦从库 */ /** 达梦从库 */
String DM_SLAVE = "dm-slave"; String DM_SLAVE = "dm-slave";
/** Oracle 主库 */ /** Oracle 主库(由数据库表 GEN_DATASOURCE_CONF 动态注册,不再从 yml 配置) */
String ORACLE_MASTER = "oracle-master"; String ORACLE_MASTER = "oracle-master";
/** Oracle 从库 */ /** Oracle 从库(由数据库表 GEN_DATASOURCE_CONF 动态注册,不再从 yml 配置) */
String ORACLE_SLAVE = "oracle-slave"; String ORACLE_SLAVE = "oracle-slave";
/** 默认数据源 */ /** 默认数据源 */
String DEFAULT = DM_MASTER; String DEFAULT = DM_MASTER;
}
/** 扩展数据源 key 前缀 */
String EXT_PREFIX = "ext-";
}

View File

@ -1,23 +1,39 @@
package com.yfd.platform.datasource; package com.yfd.platform.datasource;
import com.alibaba.druid.pool.DruidDataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import javax.sql.DataSource; import javax.sql.DataSource;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Set;
/**
* 动态数据源路由支持运行时注册/注销/热刷新
*
* @author wxy
*/
public class DynamicDataSource extends AbstractRoutingDataSource {
private static final Logger log = LoggerFactory.getLogger(DynamicDataSource.class);
/******************************
* 用途说明:
* 作者姓名: wxy
* 创建时间: 2022/9/23 17:47
******************************/
public class DynamicDataSource extends AbstractRoutingDataSource {
private static final ThreadLocal<String> contextHolder = new ThreadLocal<>(); private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, DataSource> targetDataSources) { /**
* 用户手动注册的目标数据源 yml 配置 + 数据库表注册
*/
private final Map<Object, Object> targetDataSources = new HashMap<>();
private final Object lock = new Object();
public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, DataSource> initialDataSources) {
super.setDefaultTargetDataSource(defaultTargetDataSource); super.setDefaultTargetDataSource(defaultTargetDataSource);
super.setTargetDataSources(new HashMap<Object, Object>(targetDataSources)); if (initialDataSources != null) {
super.afterPropertiesSet(); this.targetDataSources.putAll(initialDataSources);
}
rebuildResolvedDataSources();
} }
@Override @Override
@ -38,10 +54,96 @@ public class DynamicDataSource extends AbstractRoutingDataSource {
} }
/** /**
* 根据数据源 key 获取目标数据源实例 dm-master / oracle-master * 根据数据源 key 获取目标数据源实例
*/ */
public DataSource getDataSourceByKey(String key) { public DataSource getDataSourceByKey(String key) {
Map<Object, DataSource> resolved = getResolvedDataSources(); Map<Object, DataSource> resolved = getResolvedDataSources();
return resolved == null ? null : resolved.get(key); return resolved == null ? null : resolved.get(key);
} }
}
/**
* 运行时注册一个数据源线程安全立即生效
*
* @param key 路由 key "ext-oracle-01"
* @param ds 数据源实例
*/
public void register(String key, DataSource ds) {
synchronized (lock) {
// 如果已有旧数据源且不同实例关闭旧连接池
Object old = targetDataSources.get(key);
if (old instanceof DruidDataSource && old != ds) {
closeQuietly((DruidDataSource) old);
}
targetDataSources.put(key, ds);
rebuildResolvedDataSources();
log.info("数据源已注册: key={}, type={}", key, ds.getClass().getSimpleName());
}
}
/**
* 运行时注销一个数据源线程安全立即生效
*
* @param key 路由 key
* @return true-已注销并关闭连接池false-未找到该 key
*/
public boolean unregister(String key) {
synchronized (lock) {
Object removed = targetDataSources.remove(key);
if (removed == null) {
return false;
}
rebuildResolvedDataSources();
closeQuietly(removed);
log.info("数据源已注销: key={}", key);
return true;
}
}
/**
* 批量替换同名数据源先保留旧连接池注册新实例再关闭旧的
* 用于热刷新时零中断更新
*/
public void registerReplace(String key, DataSource newDs) {
synchronized (lock) {
Object old = targetDataSources.get(key);
targetDataSources.put(key, newDs);
rebuildResolvedDataSources();
closeQuietly(old);
log.info("数据源已热替换: key={}", key);
}
}
/**
* 获取所有已注册的数据源 key
*/
@SuppressWarnings("unchecked")
public Set<Object> getRegisteredKeys() {
Map<Object, DataSource> resolved = getResolvedDataSources();
return resolved == null ? Set.of() : resolved.keySet();
}
/**
* 判断 key 是否已注册
*/
public boolean containsKey(String key) {
return getDataSourceByKey(key) != null;
}
/**
* 重建路由表内部同步需由调用方保证线程安全
*/
private void rebuildResolvedDataSources() {
super.setTargetDataSources(new HashMap<>(targetDataSources));
super.afterPropertiesSet();
}
private void closeQuietly(Object ds) {
if (ds instanceof DruidDataSource) {
try {
((DruidDataSource) ds).close();
} catch (Exception e) {
log.warn("关闭旧数据源连接池异常: {}", e.getMessage());
}
}
}
}

View File

@ -14,17 +14,17 @@ import java.util.HashMap;
import java.util.Map; import java.util.Map;
/** /**
* 动态数据源配置支持 达梦(DM) Oracle 两种数据库每种各有主从(master/slave) * 动态数据源配置仅保留业务主库达梦 DM yml 配置
* <p> * <p>
* 每个数据源可通过配置项 {@code spring.datasource.druid.<key>.enabled} 按需开启/关闭 * 其他数据源 OracleMySQLPostgreSQL 通过数据库表
* 关闭后不会创建对应的连接池也不会注册到路由数据源中 * {@code GEN_DATASOURCE_CONF} 动态注册 {@link DynamicDataSourceRegistry}
* *
* @author yfd * @author yfd
*/ */
@Configuration @Configuration
public class DynamicDataSourceConfig { public class DynamicDataSourceConfig {
// ==================== 达梦(DM) 数据源 ==================== // ==================== 达梦(DM) 数据源业务主库yml 固定配置 ====================
@Bean @Bean
@ConfigurationProperties("spring.datasource.druid.dm-master") @ConfigurationProperties("spring.datasource.druid.dm-master")
@ -40,37 +40,17 @@ public class DynamicDataSourceConfig {
return newDruidDataSource(); return newDruidDataSource();
} }
// ==================== Oracle 数据源 ====================
@Bean
@ConfigurationProperties("spring.datasource.druid.oracle-master")
@ConditionalOnProperty(prefix = "spring.datasource.druid.oracle-master", name = "enabled", havingValue = "true", matchIfMissing = true)
public DataSource oracleMasterDataSource() {
return newDruidDataSource();
}
@Bean
@ConfigurationProperties("spring.datasource.druid.oracle-slave")
@ConditionalOnProperty(prefix = "spring.datasource.druid.oracle-slave", name = "enabled", havingValue = "true", matchIfMissing = true)
public DataSource oracleSlaveDataSource() {
return newDruidDataSource();
}
// ==================== 路由数据源 ==================== // ==================== 路由数据源 ====================
@Bean @Bean
@Primary @Primary
public DynamicDataSource dataSource(@Qualifier("dmMasterDataSource") ObjectProvider<DataSource> dmMaster, public DynamicDataSource dataSource(@Qualifier("dmMasterDataSource") ObjectProvider<DataSource> dmMaster,
@Qualifier("dmSlaveDataSource") ObjectProvider<DataSource> dmSlave, @Qualifier("dmSlaveDataSource") ObjectProvider<DataSource> dmSlave) {
@Qualifier("oracleMasterDataSource") ObjectProvider<DataSource> oracleMaster,
@Qualifier("oracleSlaveDataSource") ObjectProvider<DataSource> oracleSlave) {
Map<Object, DataSource> targetDataSources = new HashMap<>(); Map<Object, DataSource> targetDataSources = new HashMap<>();
putIfPresent(targetDataSources, DataSourceKeys.DM_MASTER, dmMaster); putIfPresent(targetDataSources, DataSourceKeys.DM_MASTER, dmMaster);
putIfPresent(targetDataSources, DataSourceKeys.DM_SLAVE, dmSlave); putIfPresent(targetDataSources, DataSourceKeys.DM_SLAVE, dmSlave);
putIfPresent(targetDataSources, DataSourceKeys.ORACLE_MASTER, oracleMaster);
putIfPresent(targetDataSources, DataSourceKeys.ORACLE_SLAVE, oracleSlave);
// 默认优先使用达梦主库若未开启则回退到任意一个已开启的数据源 // 默认使用达梦主库
DataSource defaultDataSource = dmMaster.getIfAvailable(); DataSource defaultDataSource = dmMaster.getIfAvailable();
if (defaultDataSource == null) { if (defaultDataSource == null) {
defaultDataSource = targetDataSources.values().stream().findFirst() defaultDataSource = targetDataSources.values().stream().findFirst()
@ -92,4 +72,4 @@ public class DynamicDataSourceConfig {
targetDataSources.put(key, dataSource); targetDataSources.put(key, dataSource);
} }
} }
} }

View File

@ -0,0 +1,106 @@
package com.yfd.platform.datasource;
import com.yfd.platform.config.ResponseResult;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.sql.DataSource;
import java.util.*;
/**
* 动态数据源管理控制器提供运行时数据源注册/注销/热刷新/列表查询等操作
* <p>
* 数据源配置存储在数据库表 {@code GEN_DATASOURCE_CONF} 通过此接口可
* <ul>
* <li>查看当前已注册的所有数据源</li>
* <li>手动注册/注销单个数据源</li>
* <li>从数据库表热刷新全部数据源</li>
* </ul>
*/
@RestController
@RequestMapping("/datasource")
@Tag(name = "动态数据源管理")
@Validated
public class DynamicDataSourceController {
@Resource
private DynamicDataSourceRegistry dynamicDataSourceRegistry;
@Resource
private DynamicDataSource dynamicDataSource;
/**
* 列出所有已注册的数据源含系统内置 dm-master/dm-slave
*/
@GetMapping("/list")
@Operation(summary = "列出所有已注册的数据源")
public ResponseResult listDataSources() {
Set<Object> keys = dynamicDataSource.getRegisteredKeys();
List<Map<String, Object>> list = new ArrayList<>(keys.size());
for (Object key : keys) {
Map<String, Object> item = new LinkedHashMap<>();
item.put("key", key);
DataSource ds = dynamicDataSource.getDataSourceByKey(key.toString());
item.put("type", ds == null ? "unknown" : ds.getClass().getSimpleName());
item.put("alive", ds != null);
list.add(item);
}
// 内置 key 排前扩展 key 排后
list.sort(Comparator.comparing(m -> {
String k = (String) m.get("key");
if (DataSourceKeys.DM_MASTER.equals(k)) return 0;
if (DataSourceKeys.DM_SLAVE.equals(k)) return 1;
return 2;
}));
return ResponseResult.successData(list);
}
/**
* 热刷新所有数据源从数据库表重新读取配置并注册
*/
@PostMapping("/refresh")
@Operation(summary = "热刷新数据源(从数据库表重新读取并注册)")
public ResponseResult refreshDataSources() {
dynamicDataSourceRegistry.refresh();
return ResponseResult.success("动态数据源热刷新完成");
}
/**
* 手动注册一个数据源仅注册到路由不持久化到数据库表
*/
@PostMapping("/register")
@Operation(summary = "手动注册一个数据源(仅运行时,不持久化到表)")
public ResponseResult registerDataSource(@RequestBody GenDatasourceConf conf) {
if (conf.getDsKey() == null || conf.getDsKey().isBlank()) {
return ResponseResult.error("dsKey 不能为空");
}
if (conf.getUrl() == null || conf.getUrl().isBlank()) {
return ResponseResult.error("url 不能为空");
}
if (dynamicDataSourceRegistry.registerRuntime(conf)) {
return ResponseResult.success("数据源手动注册成功: " + conf.getDsKey());
} else {
return ResponseResult.error("数据源注册失败: " + conf.getDsKey());
}
}
/**
* 注销一个数据源
*/
@DeleteMapping("/unregister/{key}")
@Operation(summary = "注销一个数据源")
public ResponseResult unregisterDataSource(@PathVariable("key") String key) {
if (DataSourceKeys.DM_MASTER.equals(key) || DataSourceKeys.DM_SLAVE.equals(key)) {
return ResponseResult.error("系统内置数据源不允许注销");
}
boolean removed = dynamicDataSource.unregister(key);
if (removed) {
return ResponseResult.success("数据源已注销: " + key);
} else {
return ResponseResult.error("未找到数据源: " + key);
}
}
}

View File

@ -0,0 +1,300 @@
package com.yfd.platform.datasource;
import com.alibaba.druid.pool.DruidDataSource;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* 动态数据源注册器从数据库表 {@code GEN_DATASOURCE_CONF} 读取配置
* 构建 Druid 连接池并注册到 {@link DynamicDataSource}支持运行时热刷新
* <p>
* 使用方式
* <ul>
* <li>启动时 {@link #init()} 自动注册所有已启用的数据源</li>
* <li>运行时调用 {@link #refresh()} 重新读取表并热刷新零中断</li>
* </ul>
*/
@Component
public class DynamicDataSourceRegistry {
private static final Logger log = LoggerFactory.getLogger(DynamicDataSourceRegistry.class);
/** 业务主库dm-master的 DataSource用于查询 GEN_DATASOURCE_CONF 表 */
@Resource
@Qualifier("dmMasterDataSource")
private DataSource dmMasterDataSource;
@Resource
private DynamicDataSource dynamicDataSource;
private final Object refreshLock = new Object();
/**
* 启动时初始化从数据库表加载所有已启用的数据源并注册
*/
@PostConstruct
public void init() {
try {
List<GenDatasourceConf> configs = loadConfigs();
if (configs.isEmpty()) {
log.info("GEN_DATASOURCE_CONF 表中无已启用的动态数据源配置,跳过注册");
return;
}
int success = 0;
int fail = 0;
for (GenDatasourceConf conf : configs) {
if (tryRegister(conf)) {
success++;
} else {
fail++;
}
}
log.info("动态数据源初始化完成: 成功={}, 失败={}", success, fail);
} catch (Exception e) {
log.error("动态数据源初始化失败(业务主库未就绪,稍后手动刷新即可): {}", e.getMessage());
}
}
/**
* 运行时热刷新重新读取数据库表自动注册新增/变更的数据源注销已删除/禁用的数据源
*/
public void refresh() {
synchronized (refreshLock) {
log.info("开始热刷新动态数据源...");
List<GenDatasourceConf> configs = loadConfigs();
// 1. 收集表中期望的 key
java.util.Set<String> expectedKeys = new java.util.HashSet<>();
for (GenDatasourceConf conf : configs) {
if (conf.getDsKey() != null) {
expectedKeys.add(conf.getDsKey());
}
}
// 2. 注册或替换表中数据源
int success = 0;
int fail = 0;
for (GenDatasourceConf conf : configs) {
if (tryRegister(conf)) {
success++;
} else {
fail++;
}
}
// 3. 注销表中已不存在或已禁用的数据源排除内置的 dm-master/dm-slave
java.util.Set<Object> registeredKeys = dynamicDataSource.getRegisteredKeys();
for (Object keyObj : registeredKeys) {
String key = keyObj.toString();
// 跳过系统内置数据源
if (DataSourceKeys.DM_MASTER.equals(key) || DataSourceKeys.DM_SLAVE.equals(key)) {
continue;
}
if (!expectedKeys.contains(key)) {
dynamicDataSource.unregister(key);
log.info("热刷新: 已注销数据源 key={}", key);
}
}
log.info("动态数据源热刷新完成: 成功={}, 失败={}, 注销={}", success, fail,
registeredKeys.size() - expectedKeys.size());
}
}
/**
* 从数据库表查询所有已启用的数据源配置
*/
private List<GenDatasourceConf> loadConfigs() {
JdbcTemplate jt = new JdbcTemplate(dmMasterDataSource);
String sql = "SELECT * FROM GEN_DATASOURCE_CONF WHERE NVL(IS_DELETED, 0) = 0 AND NVL(ENABLED, 1) = 1 ORDER BY ORDER_INDEX";
List<Map<String, Object>> rows = jt.queryForList(sql);
List<GenDatasourceConf> list = new ArrayList<>(rows.size());
for (Map<String, Object> row : rows) {
GenDatasourceConf conf = mapRow(row);
if (conf.getDsKey() == null || conf.getDsKey().isBlank()) {
log.warn("GEN_DATASOURCE_CONF 记录 ID={} 缺少 DS_KEY跳过", row.get("ID"));
continue;
}
list.add(conf);
}
return list;
}
/**
* 尝试注册单个数据源
*
* @return true-注册成功false-注册失败
*/
private boolean tryRegister(GenDatasourceConf conf) {
String key = conf.getDsKey().trim();
try {
DruidDataSource ds = buildDataSource(conf);
// 使用 registerReplace 热替换运行时零中断
dynamicDataSource.registerReplace(key, ds);
log.info("数据源注册成功: key={}, url={}", key, conf.getUrl());
return true;
} catch (Exception e) {
log.error("数据源注册失败: key={}, url={}, 原因={}", key, conf.getUrl(), e.getMessage(), e);
return false;
}
}
/**
* 运行时手动注册API 调用不持久化到数据库表
*
* @return true-注册成功
*/
public boolean registerRuntime(GenDatasourceConf conf) {
return tryRegister(conf);
}
/**
* 根据配置构建 Druid 数据源
*/
private DruidDataSource buildDataSource(GenDatasourceConf conf) {
DruidDataSource ds = new DruidDataSource();
// 驱动先按 URL 自动识别再按配置
String driver = conf.getDriver();
if (driver == null || driver.isBlank()) {
driver = resolveDriverFromUrl(conf.getUrl());
}
ds.setDriverClassName(driver);
ds.setUrl(conf.getUrl());
ds.setUsername(conf.getUsername());
ds.setPassword(conf.getPassword());
// 连接池大小
int maxActive = (conf.getMaxActive() != null && conf.getMaxActive() > 0)
? conf.getMaxActive() : 20;
ds.setInitialSize(Math.min(5, maxActive));
ds.setMinIdle(Math.min(5, maxActive));
ds.setMaxActive(maxActive);
// 超时
long connTimeout = (conf.getConnTimeout() != null && conf.getConnTimeout() > 0)
? conf.getConnTimeout() : 30000L;
ds.setMaxWait(connTimeout);
// 通用连接池配置
ds.setValidationQuery("SELECT 1");
ds.setValidationQueryTimeout(3);
ds.setTestWhileIdle(true);
ds.setTestOnBorrow(false);
ds.setTestOnReturn(false);
ds.setKeepAlive(true);
ds.setBreakAfterAcquireFailure(true);
ds.setConnectionErrorRetryAttempts(0);
ds.setTimeBetweenConnectErrorMillis(30000);
ds.setTimeBetweenEvictionRunsMillis(60000);
ds.setMinEvictableIdleTimeMillis(180000);
ds.setMaxEvictableIdleTimeMillis(300000);
ds.setRemoveAbandoned(true);
ds.setRemoveAbandonedTimeout(1800);
ds.setLogAbandoned(true);
try {
ds.init();
} catch (SQLException e) {
// 关闭半初始化的连接池再抛出
ds.close();
throw new IllegalStateException("初始化数据源失败: " + conf.getUrl(), e);
}
return ds;
}
/**
* 根据 URL 自动识别 JDBC 驱动类名
*/
private String resolveDriverFromUrl(String url) {
if (url == null) {
throw new IllegalArgumentException("URL 为空,无法自动识别驱动类型");
}
String lower = url.toLowerCase();
if (lower.startsWith("jdbc:dm")) {
return "dm.jdbc.driver.DmDriver";
}
if (lower.startsWith("jdbc:oracle")) {
return "oracle.jdbc.OracleDriver";
}
if (lower.startsWith("jdbc:mysql")) {
return "com.mysql.cj.jdbc.Driver";
}
if (lower.startsWith("jdbc:postgresql")) {
return "org.postgresql.Driver";
}
if (lower.startsWith("jdbc:sqlserver") || lower.startsWith("jdbc:microsoft")) {
return "com.microsoft.sqlserver.jdbc.SQLServerDriver";
}
throw new IllegalArgumentException("无法根据 URL 自动识别驱动类型: " + url);
}
/**
* 将数据库行映射为实体
*/
private GenDatasourceConf mapRow(Map<String, Object> row) {
GenDatasourceConf conf = new GenDatasourceConf();
conf.setId(strVal(row.get("ID")));
conf.setDriver(strVal(row.get("DRIVER")));
conf.setUrl(strVal(row.get("URL")));
conf.setUsername(strVal(row.get("USERNAME")));
conf.setPassword(strVal(row.get("PASSWORD")));
conf.setInternal(intVal(row.get("INTERNAL")));
conf.setOrderIndex(intVal(row.get("ORDER_INDEX")));
conf.setFilterContent(strVal(row.get("FILTER_CONTENT")));
conf.setRecordUser(strVal(row.get("RECORD_USER")));
conf.setRecordTime(dateVal(row.get("RECORD_TIME")));
conf.setModifyUser(strVal(row.get("MODIFY_USER")));
conf.setModifyTime(dateVal(row.get("MODIFY_TIME")));
conf.setIsDeleted(intVal(row.get("IS_DELETED")));
conf.setDeleteUser(strVal(row.get("DELETE_USER")));
conf.setDeleteTime(dateVal(row.get("DELETE_TIME")));
conf.setCreateDate(dateVal(row.get("CREATE_DATE")));
conf.setUpdateDate(dateVal(row.get("UPDATE_DATE")));
conf.setDelFlag(strVal(row.get("DEL_FLAG")));
// 扩展字段
conf.setDsKey(strVal(row.get("DS_KEY")));
conf.setEnabled(intVal(row.get("ENABLED")));
conf.setDsName(strVal(row.get("DS_NAME")));
conf.setDsDesc(strVal(row.get("DS_DESC")));
conf.setConnTimeout(longVal(row.get("CONN_TIMEOUT")));
conf.setMaxActive(intVal(row.get("MAX_ACTIVE")));
return conf;
}
private String strVal(Object obj) {
return obj == null ? null : obj.toString();
}
private Integer intVal(Object obj) {
if (obj == null) return null;
if (obj instanceof Number) return ((Number) obj).intValue();
try { return Integer.parseInt(obj.toString()); } catch (Exception e) { return null; }
}
private Long longVal(Object obj) {
if (obj == null) return null;
if (obj instanceof Number) return ((Number) obj).longValue();
try { return Long.parseLong(obj.toString()); } catch (Exception e) { return null; }
}
private java.util.Date dateVal(Object obj) {
if (obj == null) return null;
if (obj instanceof java.util.Date) return (java.util.Date) obj;
if (obj instanceof java.sql.Date) return new java.util.Date(((java.sql.Date) obj).getTime());
if (obj instanceof java.sql.Timestamp) return new java.util.Date(((java.sql.Timestamp) obj).getTime());
return null;
}
}

View File

@ -0,0 +1,125 @@
package com.yfd.platform.datasource;
import java.util.Date;
/**
* GEN_DATASOURCE_CONF 数据源配置表实体用于动态数据源注册
* <p>
* 字段与数据库表一一对应使用 {@link com.yfd.platform.common.MicroservicDynamicSQLMapper#getAllList}
* 动态 SQL 查询 / 手动映射不依赖 MyBatis-Plus 实体扫描
*/
public class GenDatasourceConf {
private String id;
private String driver;
private String url;
private String username;
private String password;
private Integer internal;
private Integer orderIndex;
private String filterContent;
private String recordUser;
private Date recordTime;
private String modifyUser;
private Date modifyTime;
private Integer isDeleted;
private String deleteUser;
private Date deleteTime;
private Date createDate;
private Date updateDate;
private String delFlag;
// ========== 扩展字段 ==========
/** 数据源路由 key全局唯一 */
private String dsKey;
/** 是否启用0=禁用 1=启用 */
private Integer enabled;
/** 数据源显示名称 */
private String dsName;
/** 数据源描述 */
private String dsDesc;
/** 连接超时毫秒默认30000 */
private Long connTimeout;
/** 最大活跃连接数默认20 */
private Integer maxActive;
// ==================== Getters & Setters ====================
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public String getDriver() { return driver; }
public void setDriver(String driver) { this.driver = driver; }
public String getUrl() { return url; }
public void setUrl(String url) { this.url = url; }
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
public Integer getInternal() { return internal; }
public void setInternal(Integer internal) { this.internal = internal; }
public Integer getOrderIndex() { return orderIndex; }
public void setOrderIndex(Integer orderIndex) { this.orderIndex = orderIndex; }
public String getFilterContent() { return filterContent; }
public void setFilterContent(String filterContent) { this.filterContent = filterContent; }
public String getRecordUser() { return recordUser; }
public void setRecordUser(String recordUser) { this.recordUser = recordUser; }
public Date getRecordTime() { return recordTime; }
public void setRecordTime(Date recordTime) { this.recordTime = recordTime; }
public String getModifyUser() { return modifyUser; }
public void setModifyUser(String modifyUser) { this.modifyUser = modifyUser; }
public Date getModifyTime() { return modifyTime; }
public void setModifyTime(Date modifyTime) { this.modifyTime = modifyTime; }
public Integer getIsDeleted() { return isDeleted; }
public void setIsDeleted(Integer isDeleted) { this.isDeleted = isDeleted; }
public String getDeleteUser() { return deleteUser; }
public void setDeleteUser(String deleteUser) { this.deleteUser = deleteUser; }
public Date getDeleteTime() { return deleteTime; }
public void setDeleteTime(Date deleteTime) { this.deleteTime = deleteTime; }
public Date getCreateDate() { return createDate; }
public void setCreateDate(Date createDate) { this.createDate = createDate; }
public Date getUpdateDate() { return updateDate; }
public void setUpdateDate(Date updateDate) { this.updateDate = updateDate; }
public String getDelFlag() { return delFlag; }
public void setDelFlag(String delFlag) { this.delFlag = delFlag; }
public String getDsKey() { return dsKey; }
public void setDsKey(String dsKey) { this.dsKey = dsKey; }
public Integer getEnabled() { return enabled; }
public void setEnabled(Integer enabled) { this.enabled = enabled; }
public String getDsName() { return dsName; }
public void setDsName(String dsName) { this.dsName = dsName; }
public String getDsDesc() { return dsDesc; }
public void setDsDesc(String dsDesc) { this.dsDesc = dsDesc; }
public Long getConnTimeout() { return connTimeout; }
public void setConnTimeout(Long connTimeout) { this.connTimeout = connTimeout; }
public Integer getMaxActive() { return maxActive; }
public void setMaxActive(Integer maxActive) { this.maxActive = maxActive; }
}

View File

@ -69,70 +69,6 @@ spring:
pool-prepared-statements: true pool-prepared-statements: true
max-open-prepared-statements: 100 max-open-prepared-statements: 100
max-pool-prepared-statement-per-connection-size: 100 max-pool-prepared-statement-per-connection-size: 100
# ==================== Oracle 数据源 ====================
# enabled=false 时不创建连接池,也不注册到路由数据源(按需开启)
oracle-master:
enabled: false
driverClassName: oracle.jdbc.OracleDriver
url: "${DB_ORACLE_MASTER_URL:jdbc:oracle:thin:@172.16.31.172:1521/SDLYZ}"
username: "${DB_ORACLE_MASTER_USERNAME:SDLY_QX}"
password: "${DB_ORACLE_MASTER_PASSWORD:jNHnqv3hH7}"
initial-size: 5
min-idle: 5
max-active: 20
max-wait: 30000
async-init: true
keep-alive-between-time-millis: 120000
time-between-eviction-runs-millis: 60000
min-evictable-idle-time-millis: 180000
max-evictable-idle-time-millis: 300000
phy-timeout-millis: 25200000
validation-query: SELECT 1 FROM DUAL
validation-query-timeout: 3
test-while-idle: true
test-on-borrow: false
test-on-return: false
keep-alive: true
remove-abandoned: true
remove-abandoned-timeout: 1800
log-abandoned: true
break-after-acquire-failure: true
time-between-connect-error-millis: 30000
pool-prepared-statements: true
max-open-prepared-statements: 100
max-pool-prepared-statement-per-connection-size: 100
connection-properties: oracle.net.CONNECT_TIMEOUT=10000;oracle.jdbc.ReadTimeout=60000;oracle.net.READ_TIMEOUT=60000
oracle-slave:
enabled: false
driverClassName: oracle.jdbc.OracleDriver
url: "${DB_ORACLE_SLAVE_URL:jdbc:oracle:thin:@172.16.31.172:1521/SDLYZ}"
username: "${DB_ORACLE_SLAVE_USERNAME:SDLY_QX}"
password: "${DB_ORACLE_SLAVE_PASSWORD:jNHnqv3hH7}"
initial-size: 5
min-idle: 5
max-active: 20
max-wait: 30000
async-init: true
keep-alive-between-time-millis: 120000
time-between-eviction-runs-millis: 60000
min-evictable-idle-time-millis: 180000
max-evictable-idle-time-millis: 300000
phy-timeout-millis: 25200000
validation-query: SELECT 1 FROM DUAL
validation-query-timeout: 3
test-while-idle: true
test-on-borrow: false
test-on-return: false
keep-alive: true
remove-abandoned: true
remove-abandoned-timeout: 1800
log-abandoned: true
break-after-acquire-failure: true
time-between-connect-error-millis: 30000
pool-prepared-statements: true
max-open-prepared-statements: 100
max-pool-prepared-statement-per-connection-size: 100
connection-properties: oracle.net.CONNECT_TIMEOUT=10000;oracle.jdbc.ReadTimeout=60000;oracle.net.READ_TIMEOUT=60000
filter: filter:
stat: stat:
enabled: true enabled: true

View File

@ -69,70 +69,6 @@ spring:
pool-prepared-statements: true pool-prepared-statements: true
max-open-prepared-statements: 100 max-open-prepared-statements: 100
max-pool-prepared-statement-per-connection-size: 100 max-pool-prepared-statement-per-connection-size: 100
# ==================== Oracle 数据源 ====================
# enabled=false 时不创建连接池,也不注册到路由数据源(按需开启)
oracle-master:
enabled: false
driverClassName: oracle.jdbc.OracleDriver
url: "${DB_ORACLE_MASTER_URL:jdbc:oracle:thin:@172.16.31.172:1521/SDLYZ}"
username: "${DB_ORACLE_MASTER_USERNAME:SDLY_QX}"
password: "${DB_ORACLE_MASTER_PASSWORD:jNHnqv3hH7}"
initial-size: 5
min-idle: 5
max-active: 20
max-wait: 30000
async-init: true
keep-alive-between-time-millis: 120000
time-between-eviction-runs-millis: 60000
min-evictable-idle-time-millis: 180000
max-evictable-idle-time-millis: 300000
phy-timeout-millis: 25200000
validation-query: SELECT 1 FROM DUAL
validation-query-timeout: 3
test-while-idle: true
test-on-borrow: false
test-on-return: false
keep-alive: true
remove-abandoned: true
remove-abandoned-timeout: 1800
log-abandoned: true
break-after-acquire-failure: true
time-between-connect-error-millis: 30000
pool-prepared-statements: true
max-open-prepared-statements: 100
max-pool-prepared-statement-per-connection-size: 100
connection-properties: oracle.net.CONNECT_TIMEOUT=10000;oracle.jdbc.ReadTimeout=60000;oracle.net.READ_TIMEOUT=60000
oracle-slave:
enabled: false
driverClassName: oracle.jdbc.OracleDriver
url: "${DB_ORACLE_SLAVE_URL:jdbc:oracle:thin:@172.16.31.172:1521/SDLYZ}"
username: "${DB_ORACLE_SLAVE_USERNAME:SDLY_QX}"
password: "${DB_ORACLE_SLAVE_PASSWORD:jNHnqv3hH7}"
initial-size: 5
min-idle: 5
max-active: 20
max-wait: 30000
async-init: true
keep-alive-between-time-millis: 120000
time-between-eviction-runs-millis: 60000
min-evictable-idle-time-millis: 180000
max-evictable-idle-time-millis: 300000
phy-timeout-millis: 25200000
validation-query: SELECT 1 FROM DUAL
validation-query-timeout: 3
test-while-idle: true
test-on-borrow: false
test-on-return: false
keep-alive: true
remove-abandoned: true
remove-abandoned-timeout: 1800
log-abandoned: true
break-after-acquire-failure: true
time-between-connect-error-millis: 30000
pool-prepared-statements: true
max-open-prepared-statements: 100
max-pool-prepared-statement-per-connection-size: 100
connection-properties: oracle.net.CONNECT_TIMEOUT=10000;oracle.jdbc.ReadTimeout=60000;oracle.net.READ_TIMEOUT=60000
filter: filter:
stat: stat:
enabled: true enabled: true