add
This commit is contained in:
8
pom.xml
8
pom.xml
@ -61,6 +61,8 @@
|
|||||||
<jakarta.version>3.0.2</jakarta.version>
|
<jakarta.version>3.0.2</jakarta.version>
|
||||||
<!-- ip离线库 -->
|
<!-- ip离线库 -->
|
||||||
<ip2region.version>2.7.0</ip2region.version>
|
<ip2region.version>2.7.0</ip2region.version>
|
||||||
|
<!-- 模板引擎 -->
|
||||||
|
<velocity.version>2.3</velocity.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencyManagement>
|
<dependencyManagement>
|
||||||
@ -137,6 +139,12 @@
|
|||||||
<artifactId>ip2region</artifactId>
|
<artifactId>ip2region</artifactId>
|
||||||
<version>${ip2region.version}</version>
|
<version>${ip2region.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<!-- 模板引擎 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.velocity</groupId>
|
||||||
|
<artifactId>velocity-engine-core</artifactId>
|
||||||
|
<version>${velocity.version}</version>
|
||||||
|
</dependency>
|
||||||
<!-- ======================== system module ======================== -->
|
<!-- ======================== system module ======================== -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.qiaoba</groupId>
|
<groupId>com.qiaoba</groupId>
|
||||||
|
@ -37,4 +37,4 @@ mybatis-plus:
|
|||||||
|
|
||||||
logging:
|
logging:
|
||||||
level:
|
level:
|
||||||
com.qiaoba: trace #开发环境输出sql日志
|
com.qiaoba: info #开发环境输出sql日志
|
||||||
|
@ -82,4 +82,15 @@ public enum DataBaseEnum {
|
|||||||
}
|
}
|
||||||
throw new ServiceException(StrUtil.format("未找到数据库checkSql, Type: {}", type));
|
throw new ServiceException(StrUtil.format("未找到数据库checkSql, Type: {}", type));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String getIp(String url, String driver) {
|
||||||
|
for (DataBaseEnum dataBaseEnum : values()) {
|
||||||
|
if (dataBaseEnum.getDriver().equals(driver)) {
|
||||||
|
url = url.replaceFirst(url.substring(0, dataBaseEnum.url.indexOf(StrUtil.EMPTY_JSON)), "");
|
||||||
|
return url.split(StrUtil.COLON)[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new ServiceException(StrUtil.format("未找到数据库IP, Driver: {}", driver));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ import com.qiaoba.common.base.constants.TenantConstant;
|
|||||||
import com.qiaoba.common.base.context.BaseContext;
|
import com.qiaoba.common.base.context.BaseContext;
|
||||||
import com.qiaoba.common.base.enums.BaseEnum;
|
import com.qiaoba.common.base.enums.BaseEnum;
|
||||||
import com.qiaoba.common.database.entity.DynamicDataSource;
|
import com.qiaoba.common.database.entity.DynamicDataSource;
|
||||||
|
import com.qiaoba.common.database.monitor.NotOnlineDatasourceMonitor;
|
||||||
import com.qiaoba.common.database.properties.DataSourceProperties;
|
import com.qiaoba.common.database.properties.DataSourceProperties;
|
||||||
import com.qiaoba.common.database.service.DynamicDatasourceService;
|
import com.qiaoba.common.database.service.DynamicDatasourceService;
|
||||||
import com.qiaoba.common.database.utils.JdbcUtil;
|
import com.qiaoba.common.database.utils.JdbcUtil;
|
||||||
@ -89,13 +90,28 @@ public class DynamicDataSourceConfig {
|
|||||||
|
|
||||||
private void initDefault() {
|
private void initDefault() {
|
||||||
List<DynamicDataSource> dataSources = dataSourceProperties.getDataSources();
|
List<DynamicDataSource> dataSources = dataSourceProperties.getDataSources();
|
||||||
// 0索引作为主数据源
|
for (int i = 0; i < dataSources.size(); i++) {
|
||||||
addPrimaryMap(TenantConstant.DEFAULT_TENANT_ID, buildDataSource(TenantConstant.DEFAULT_TENANT_ID, dataSources.get(0)));
|
// 0索引作为主数据源
|
||||||
dataSources.remove(0);
|
Object dataSource = buildDataSource(TenantConstant.DEFAULT_TENANT_ID, dataSources.get(i));
|
||||||
// 非0索引的备用
|
if (Objects.isNull(dataSource)) {
|
||||||
|
// 默认的主数据源挂了
|
||||||
|
// 加入到错误数据源Map 等待重试
|
||||||
|
NotOnlineDatasourceMonitor.addErrorDatasource(TenantConstant.DEFAULT_TENANT_ID, dataSources.get(i));
|
||||||
|
dataSources.remove(i);
|
||||||
|
} else {
|
||||||
|
addPrimaryMap(TenantConstant.DEFAULT_TENANT_ID, dataSource);
|
||||||
|
dataSources.remove(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CollUtil.isEmpty(PRIMARY_DATASOURCE_MAP)) {
|
||||||
|
log.error("主系统配置数据源全部无效, 请检查 yml 中相关配置");
|
||||||
|
}
|
||||||
|
// 其他数据源备用
|
||||||
addBackupMap(TenantConstant.DEFAULT_TENANT_ID, dataSources);
|
addBackupMap(TenantConstant.DEFAULT_TENANT_ID, dataSources);
|
||||||
// 刷新数据源
|
// 刷新数据源
|
||||||
dataSource.freshDataSource(PRIMARY_DATASOURCE_MAP);
|
this.dataSource.freshDataSource(PRIMARY_DATASOURCE_MAP);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initTenant() {
|
private void initTenant() {
|
||||||
@ -106,10 +122,19 @@ public class DynamicDataSourceConfig {
|
|||||||
for (int i = 0; i < dataSources.size(); i++) {
|
for (int i = 0; i < dataSources.size(); i++) {
|
||||||
DynamicDataSource dynamicDataSource = dataSources.get(i);
|
DynamicDataSource dynamicDataSource = dataSources.get(i);
|
||||||
if (BaseEnum.YES.getCode().equals(dynamicDataSource.getIsPrimary())) {
|
if (BaseEnum.YES.getCode().equals(dynamicDataSource.getIsPrimary())) {
|
||||||
addPrimaryMap(tenantId, buildDataSource(dataSources.get(i).getTenantId(), dataSources.get(i)));
|
Object dataSource = buildDataSource(dataSources.get(i).getTenantId(), dataSources.get(i));
|
||||||
// 去除主要数据源,剩下皆为备用数据源
|
if (Objects.isNull(dataSource)) {
|
||||||
dataSources.remove(i);
|
// 默认的主数据源挂了
|
||||||
break;
|
// 加入到错误数据源Map 等待重试
|
||||||
|
NotOnlineDatasourceMonitor.addErrorDatasource(tenantId, dataSources.get(i));
|
||||||
|
// 在数据源集合中删除, 防止将错误的数据源加载到备用数据源中
|
||||||
|
dataSources.remove(i);
|
||||||
|
} else {
|
||||||
|
addPrimaryMap(tenantId, dataSource);
|
||||||
|
// 去除主要数据源,剩下皆为备用数据源
|
||||||
|
dataSources.remove(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 备用数据源
|
// 备用数据源
|
||||||
@ -129,6 +154,17 @@ public class DynamicDataSourceConfig {
|
|||||||
|
|
||||||
public void changePrimaryDatasource(String tenantId, Object datasource) {
|
public void changePrimaryDatasource(String tenantId, Object datasource) {
|
||||||
PRIMARY_DATASOURCE_MAP.put(tenantId, datasource);
|
PRIMARY_DATASOURCE_MAP.put(tenantId, datasource);
|
||||||
|
// 将数据源的类型保存
|
||||||
|
DruidPooledConnection connection = null;
|
||||||
|
try {
|
||||||
|
connection = ((DruidDataSource) datasource).getConnection();
|
||||||
|
TENANT_DATASOURCE_TYPE_MAP.put(tenantId, connection.getMetaData().getDatabaseProductName());
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} finally {
|
||||||
|
// 归还 connection
|
||||||
|
IoUtil.close(connection);
|
||||||
|
}
|
||||||
dataSource.freshDataSource(PRIMARY_DATASOURCE_MAP);
|
dataSource.freshDataSource(PRIMARY_DATASOURCE_MAP);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,117 @@
|
|||||||
|
package com.qiaoba.common.database.monitor;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
|
import cn.hutool.core.collection.ListUtil;
|
||||||
|
import com.baomidou.lock.LockInfo;
|
||||||
|
import com.baomidou.lock.LockTemplate;
|
||||||
|
import com.qiaoba.common.database.config.DynamicDataSourceConfig;
|
||||||
|
import com.qiaoba.common.database.entity.DynamicDataSource;
|
||||||
|
import com.qiaoba.common.database.utils.JdbcUtil;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import javax.annotation.PostConstruct;
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 不在线的数据源监控
|
||||||
|
* 尝试连接,如果连接成功,加入到备用数据源
|
||||||
|
*
|
||||||
|
* @author ailanyin
|
||||||
|
* @version 1.0
|
||||||
|
* @since 2023/6/26 10:46
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
@Slf4j
|
||||||
|
public class NotOnlineDatasourceMonitor {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 错误数据源(连接中断)
|
||||||
|
* <p>
|
||||||
|
* key: 租户ID
|
||||||
|
* value: 错误数据源 list
|
||||||
|
*/
|
||||||
|
public static Map<String, List<DynamicDataSource>> ERROR_DATASOURCE_MAP = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
private static final String LOCK_KEY = "lock4j:notOnlineDatasourceMonitor";
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private LockTemplate lockTemplate;
|
||||||
|
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
public void init() {
|
||||||
|
// 10s 运行一次
|
||||||
|
new Timer().schedule(new TimerTask() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
// 项目启动时加载数据源还未完成
|
||||||
|
if (!DynamicDataSourceConfig.COMPLETE_LOAD_DATASOURCE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 没有暂时失联的数据源, 直接结束
|
||||||
|
if (CollUtil.isEmpty(ERROR_DATASOURCE_MAP)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// expire = -1 锁自动续期, 防止数据源过多或异常等待, 超过默认锁 30s
|
||||||
|
final LockInfo lockInfo = lockTemplate.lock(LOCK_KEY, -1, 1000);
|
||||||
|
//申请锁失败 说明集群中其他设备正在执行监控
|
||||||
|
if (null == lockInfo) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//申请锁成功
|
||||||
|
try {
|
||||||
|
log.trace("开始-[错误数据源重试]-线程, 时间: {}", new Date());
|
||||||
|
tryConnect();
|
||||||
|
log.trace("结束-[错误数据源重试]-线程, 时间: {}", new Date());
|
||||||
|
} finally {
|
||||||
|
// 释放锁
|
||||||
|
lockTemplate.releaseLock(lockInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}, 0, 10 * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void tryConnect() {
|
||||||
|
Set<String> tenantIds = ERROR_DATASOURCE_MAP.keySet();
|
||||||
|
for (String tenantId : tenantIds) {
|
||||||
|
List<DynamicDataSource> errorDatasourceList = ERROR_DATASOURCE_MAP.get(tenantId);
|
||||||
|
for (int i = 0; i < errorDatasourceList.size(); i++) {
|
||||||
|
DynamicDataSource errorDatasource = errorDatasourceList.get(i);
|
||||||
|
// 说明连接成功
|
||||||
|
boolean check = JdbcUtil.checkConnect(errorDatasource.getDriver(), errorDatasource.getUrl(), errorDatasource.getUsername(), errorDatasource.getPassword());
|
||||||
|
if (check) {
|
||||||
|
log.info("数据源重连成功, Url: {}", errorDatasource.getUrl());
|
||||||
|
// 从errorMap中删除
|
||||||
|
errorDatasourceList.remove(errorDatasource);
|
||||||
|
if (CollUtil.isEmpty(errorDatasourceList)) {
|
||||||
|
ERROR_DATASOURCE_MAP.remove(tenantId);
|
||||||
|
}
|
||||||
|
// 加入到备用Map中
|
||||||
|
addBackupMap(tenantId, errorDatasource);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addBackupMap(String tenantId, DynamicDataSource dataSource) {
|
||||||
|
List<DynamicDataSource> dataSourceList = DynamicDataSourceConfig.BACKUP_DATASOURCE_MAP.get(tenantId);
|
||||||
|
if (CollUtil.isEmpty(dataSourceList)) {
|
||||||
|
DynamicDataSourceConfig.BACKUP_DATASOURCE_MAP.put(tenantId, ListUtil.toList(dataSource));
|
||||||
|
} else {
|
||||||
|
dataSourceList.add(dataSource);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void addErrorDatasource(String tenantId, DynamicDataSource dataSource) {
|
||||||
|
List<DynamicDataSource> errorDataSourceList = NotOnlineDatasourceMonitor.ERROR_DATASOURCE_MAP.get(tenantId);
|
||||||
|
if (CollUtil.isEmpty(errorDataSourceList)) {
|
||||||
|
NotOnlineDatasourceMonitor.ERROR_DATASOURCE_MAP.put(tenantId, ListUtil.toList(dataSource));
|
||||||
|
} else {
|
||||||
|
errorDataSourceList.add(dataSource);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
package com.qiaoba.common.database.monitor;
|
package com.qiaoba.common.database.monitor;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
import cn.hutool.core.io.IoUtil;
|
import cn.hutool.core.io.IoUtil;
|
||||||
import com.alibaba.druid.pool.DruidDataSource;
|
import com.alibaba.druid.pool.DruidDataSource;
|
||||||
import com.baomidou.lock.LockInfo;
|
import com.baomidou.lock.LockInfo;
|
||||||
@ -9,39 +10,40 @@ import com.qiaoba.common.base.enums.DataBaseEnum;
|
|||||||
import com.qiaoba.common.database.config.DynamicDataSourceConfig;
|
import com.qiaoba.common.database.config.DynamicDataSourceConfig;
|
||||||
import com.qiaoba.common.database.entity.DynamicDataSource;
|
import com.qiaoba.common.database.entity.DynamicDataSource;
|
||||||
import com.qiaoba.common.database.service.DynamicDatasourceService;
|
import com.qiaoba.common.database.service.DynamicDatasourceService;
|
||||||
|
import com.qiaoba.common.database.utils.JdbcUtil;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
import javax.annotation.PostConstruct;
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
import java.sql.ResultSet;
|
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.sql.Statement;
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 监控-数据源连接监控
|
* 在线的数据源监控
|
||||||
|
* 尝试连接, 如果连接不成功, 替换可用数据源, 将失败数据源加入到错误数据源, 等待重试
|
||||||
*
|
*
|
||||||
* @author ailanyin
|
* @author ailanyin
|
||||||
* @version 1.0
|
* @version 1.0
|
||||||
* @since 2023/6/14 14:48
|
* @since 2023/6/26 10:46
|
||||||
*/
|
*/
|
||||||
@Component
|
@Component
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class DatasourceConnectionMonitor {
|
public class OnlineDatasourceMonitor {
|
||||||
|
|
||||||
|
|
||||||
|
private static final String LOCK_KEY = "lock4j:onlineDatasourceMonitor";
|
||||||
|
private static Map<String, String> WAIT_UPDATE_DATASOURCE_STATUS = new ConcurrentHashMap<>();
|
||||||
|
private static Map<String, List<String>> WAIT_ADD_ERROR_MAP = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private LockTemplate lockTemplate;
|
||||||
@Resource
|
@Resource
|
||||||
private DynamicDataSourceConfig dynamicDataSourceConfig;
|
private DynamicDataSourceConfig dynamicDataSourceConfig;
|
||||||
@Resource
|
@Resource
|
||||||
private DynamicDatasourceService dynamicDatasourceService;
|
private DynamicDatasourceService dynamicDatasourceService;
|
||||||
@Resource
|
|
||||||
private LockTemplate lockTemplate;
|
|
||||||
|
|
||||||
private static Map<String, String> WAIT_UPDATE_DATASOURCE_STATUS = new ConcurrentHashMap<>();
|
|
||||||
|
|
||||||
private static final String LOCK_KEY = "lock4j:datasourceConnectionMonitor";
|
|
||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
public void init() {
|
public void init() {
|
||||||
@ -49,7 +51,7 @@ public class DatasourceConnectionMonitor {
|
|||||||
new Timer().schedule(new TimerTask() {
|
new Timer().schedule(new TimerTask() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
// 项目加载数据源还未完成
|
// 项目启动时加载数据源还未完成
|
||||||
if (!DynamicDataSourceConfig.COMPLETE_LOAD_DATASOURCE) {
|
if (!DynamicDataSourceConfig.COMPLETE_LOAD_DATASOURCE) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -62,8 +64,9 @@ public class DatasourceConnectionMonitor {
|
|||||||
}
|
}
|
||||||
//申请锁成功
|
//申请锁成功
|
||||||
try {
|
try {
|
||||||
// 执行监控
|
log.trace("开始运行数据源监控线程, 时间: {}", new Date());
|
||||||
datasourceConnectionMonitor();
|
tryConnect();
|
||||||
|
log.trace("结束运行数据源监控线程, 时间: {}", new Date());
|
||||||
} finally {
|
} finally {
|
||||||
// 释放锁
|
// 释放锁
|
||||||
lockTemplate.releaseLock(lockInfo);
|
lockTemplate.releaseLock(lockInfo);
|
||||||
@ -73,83 +76,62 @@ public class DatasourceConnectionMonitor {
|
|||||||
}, 0, 1000);
|
}, 0, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private void tryConnect() {
|
||||||
* 核心监控内容
|
|
||||||
*/
|
|
||||||
private void datasourceConnectionMonitor() {
|
|
||||||
log.trace("开始运行数据源监控线程, 时间: {}", new Date());
|
|
||||||
for (String tenantId : DynamicDataSourceConfig.TENANT_IDS) {
|
for (String tenantId : DynamicDataSourceConfig.TENANT_IDS) {
|
||||||
Object primary = DynamicDataSourceConfig.PRIMARY_DATASOURCE_MAP.get(tenantId);
|
Object primary = DynamicDataSourceConfig.PRIMARY_DATASOURCE_MAP.get(tenantId);
|
||||||
|
DynamicDataSourceConfig.BACKUP_DATASOURCE_MAP.get(tenantId);
|
||||||
if (Objects.isNull(primary)) {
|
if (Objects.isNull(primary)) {
|
||||||
// 说明初始化主要数据源的时候出错
|
// 说明初始化主要数据源的时候出错
|
||||||
log.error("租户[{}]-目前主数据源异常, 开始切换备用数据源", tenantId);
|
log.error("租户[{}]-目前主数据源异常, 开始切换备用数据源", tenantId);
|
||||||
// 切换备用数据源
|
// 切换备用数据源为主数据源
|
||||||
changePrimary(tenantId);
|
backToPrimary(tenantId);
|
||||||
} else {
|
continue;
|
||||||
DruidDataSource dataSource = (DruidDataSource) primary;
|
}
|
||||||
Connection connection = null;
|
|
||||||
try {
|
|
||||||
connection = dataSource.getConnection();
|
|
||||||
if (check(connection, tenantId)) {
|
|
||||||
// 说明数据源正常
|
|
||||||
log.trace("租户[{}]-目前主数据源正常, 无需切换数据源", tenantId);
|
|
||||||
// 主数据 处理任务
|
|
||||||
if (TenantConstant.DEFAULT_TENANT_ID.equals(tenantId)) {
|
|
||||||
handleJob();
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
log.error("租户[{}]-目前主数据源异常, 开始切换备用数据源", tenantId);
|
DruidDataSource dataSource = (DruidDataSource) primary;
|
||||||
// 主数据源异常 切换备用数据源
|
Connection connection = null;
|
||||||
if (changePrimary(tenantId)) {
|
try {
|
||||||
// 备用切换成功, 关闭原有异常数据源
|
connection = dataSource.getConnection();
|
||||||
IoUtil.close(dataSource);
|
if (JdbcUtil.checkConnect(connection)) {
|
||||||
|
// 说明数据源正常
|
||||||
|
log.trace("租户[{}]-目前主数据源正常, 无需切换数据源", tenantId);
|
||||||
|
// 系统默认主数据源 处理任务
|
||||||
|
if (TenantConstant.DEFAULT_TENANT_ID.equals(tenantId)) {
|
||||||
|
handleUpdateDatasourceStatus();
|
||||||
|
handleErrorDatasource();
|
||||||
}
|
}
|
||||||
} catch (SQLException e) {
|
continue;
|
||||||
|
|
||||||
} finally {
|
|
||||||
IoUtil.close(connection);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.error("租户[{}]-目前主数据源异常, 开始切换备用数据源", tenantId);
|
||||||
|
// 主数据源异常 切换备用数据源
|
||||||
|
if (!backToPrimary(tenantId)) {
|
||||||
|
// 备用切换失败
|
||||||
|
// 关闭原有异常数据源
|
||||||
|
IoUtil.close(dataSource);
|
||||||
|
// 在数据源Map中删除
|
||||||
|
DynamicDataSourceConfig.PRIMARY_DATASOURCE_MAP.remove(tenantId);
|
||||||
|
}
|
||||||
|
// 将原有异常数据源加入到错误数据源Map, 等待重试
|
||||||
|
addErrorDatasource(tenantId, dataSource);
|
||||||
|
|
||||||
|
} catch (SQLException e) {
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
IoUtil.close(connection);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.trace("结束运行数据源监控线程, 时间: {}", new Date());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 检查数据源连接可用性
|
* 切换备用数据源为主数据源
|
||||||
*
|
|
||||||
* @param conn 数据源
|
|
||||||
* @param tenantId 租户ID
|
|
||||||
* @return 结果
|
|
||||||
*/
|
|
||||||
private Boolean check(Connection conn, String tenantId) {
|
|
||||||
Statement stmt = null;
|
|
||||||
ResultSet rs = null;
|
|
||||||
try {
|
|
||||||
stmt = conn.createStatement();
|
|
||||||
// 允许 2s 延时
|
|
||||||
stmt.setQueryTimeout(2);
|
|
||||||
rs = stmt.executeQuery(DataBaseEnum.getCheckSql(DynamicDataSourceConfig.TENANT_DATASOURCE_TYPE_MAP.get(tenantId)));
|
|
||||||
return true;
|
|
||||||
} catch (Exception e) {
|
|
||||||
return false;
|
|
||||||
} finally {
|
|
||||||
IoUtil.close(rs);
|
|
||||||
IoUtil.close(stmt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 切换主数据源
|
|
||||||
*
|
*
|
||||||
* @param tenantId 租户ID
|
* @param tenantId 租户ID
|
||||||
*/
|
*/
|
||||||
private Boolean changePrimary(String tenantId) {
|
private Boolean backToPrimary(String tenantId) {
|
||||||
// 备用数据源
|
// 备用数据源
|
||||||
List<DynamicDataSource> dataSources = DynamicDataSourceConfig.BACKUP_DATASOURCE_MAP.get(tenantId);
|
List<DynamicDataSource> dataSources = DynamicDataSourceConfig.BACKUP_DATASOURCE_MAP.get(tenantId);
|
||||||
if (Objects.isNull(dataSources)) {
|
if (CollUtil.isEmpty(dataSources)) {
|
||||||
log.error("租户:[{}]切换备用数据源失败, 原因: 没有备用数据源", tenantId);
|
log.error("租户:[{}]切换备用数据源失败, 原因: 没有备用数据源", tenantId);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -184,7 +166,7 @@ public class DatasourceConnectionMonitor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleJob() {
|
private void handleUpdateDatasourceStatus() {
|
||||||
Set<String> keys = WAIT_UPDATE_DATASOURCE_STATUS.keySet();
|
Set<String> keys = WAIT_UPDATE_DATASOURCE_STATUS.keySet();
|
||||||
for (String key : keys) {
|
for (String key : keys) {
|
||||||
try {
|
try {
|
||||||
@ -195,8 +177,46 @@ public class DatasourceConnectionMonitor {
|
|||||||
WAIT_UPDATE_DATASOURCE_STATUS.remove(key);
|
WAIT_UPDATE_DATASOURCE_STATUS.remove(key);
|
||||||
log.info("更新数据库中租户数据源状态完成, 租户ID: {}", key);
|
log.info("更新数据库中租户数据源状态完成, 租户ID: {}", key);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("更新数据库中租户数据源状态完成, 租户ID: {}, 失败原因: {}", key, e.getMessage());
|
log.error("更新数据库中租户数据源状态未完成, 租户ID: {}, 失败原因: {}", key, e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void handleErrorDatasource() {
|
||||||
|
Set<String> tenantIds = WAIT_ADD_ERROR_MAP.keySet();
|
||||||
|
for (String tenantId : tenantIds) {
|
||||||
|
List<String> ipList = WAIT_ADD_ERROR_MAP.get(tenantId);
|
||||||
|
for (String ip : ipList) {
|
||||||
|
NotOnlineDatasourceMonitor.addErrorDatasource(tenantId, dynamicDatasourceService.selectByIp(tenantId, ip));
|
||||||
|
}
|
||||||
|
WAIT_ADD_ERROR_MAP.remove(tenantId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addErrorDatasource(String tenantId, DruidDataSource dataSource) {
|
||||||
|
// 主系统
|
||||||
|
if (TenantConstant.DEFAULT_TENANT_ID.equals(tenantId)) {
|
||||||
|
DynamicDataSource dynamicDataSource = new DynamicDataSource();
|
||||||
|
dynamicDataSource.setPassword(dataSource.getPassword());
|
||||||
|
dynamicDataSource.setUsername(dataSource.getUsername());
|
||||||
|
dynamicDataSource.setUrl(dataSource.getUrl());
|
||||||
|
dynamicDataSource.setDriver(dataSource.getDriverClassName());
|
||||||
|
dynamicDataSource.setTenantId(tenantId);
|
||||||
|
dynamicDataSource.setInitialSize(dataSource.getInitialSize());
|
||||||
|
dynamicDataSource.setMaxActive(dataSource.getMaxActive());
|
||||||
|
dynamicDataSource.setMinIdle(dataSource.getMinIdle());
|
||||||
|
NotOnlineDatasourceMonitor.addErrorDatasource(tenantId, dynamicDataSource);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 普通租户
|
||||||
|
String ip = DataBaseEnum.getIp(dataSource.getUrl(), dataSource.getDriverClassName());
|
||||||
|
List<String> errorIpList = WAIT_ADD_ERROR_MAP.get(tenantId);
|
||||||
|
if (CollUtil.isEmpty(errorIpList)) {
|
||||||
|
WAIT_ADD_ERROR_MAP.put(tenantId, CollUtil.toList(ip));
|
||||||
|
} else {
|
||||||
|
errorIpList.add(ip);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -28,4 +28,13 @@ public interface DynamicDatasourceService {
|
|||||||
* @param datasourceId 数据源ID
|
* @param datasourceId 数据源ID
|
||||||
*/
|
*/
|
||||||
void changePrimaryDatasource(String tenantId, String datasourceId);
|
void changePrimaryDatasource(String tenantId, String datasourceId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过IP查询
|
||||||
|
*
|
||||||
|
* @param tenantId tenantId
|
||||||
|
* @param ip ip
|
||||||
|
* @return obj
|
||||||
|
*/
|
||||||
|
DynamicDataSource selectByIp(String tenantId, String ip);
|
||||||
}
|
}
|
||||||
|
@ -2,11 +2,15 @@ package com.qiaoba.common.database.utils;
|
|||||||
|
|
||||||
import cn.hutool.core.io.IoUtil;
|
import cn.hutool.core.io.IoUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import com.qiaoba.common.base.enums.DataBaseEnum;
|
||||||
import com.qiaoba.common.base.exceptions.ServiceException;
|
import com.qiaoba.common.base.exceptions.ServiceException;
|
||||||
|
import com.qiaoba.common.database.config.DynamicDataSourceConfig;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
import java.sql.DriverManager;
|
import java.sql.DriverManager;
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.Statement;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* JdbcUtil
|
* JdbcUtil
|
||||||
@ -42,6 +46,29 @@ public class JdbcUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查数据源连接可用性
|
||||||
|
*
|
||||||
|
* @param conn 数据源
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
public static boolean checkConnect(Connection conn) {
|
||||||
|
Statement stmt = null;
|
||||||
|
ResultSet rs = null;
|
||||||
|
try {
|
||||||
|
stmt = conn.createStatement();
|
||||||
|
// 允许 2s 延时
|
||||||
|
stmt.setQueryTimeout(2);
|
||||||
|
rs = stmt.executeQuery(DataBaseEnum.getCheckSql(conn.getMetaData().getDatabaseProductName()));
|
||||||
|
return true;
|
||||||
|
} catch (Exception e) {
|
||||||
|
return false;
|
||||||
|
} finally {
|
||||||
|
IoUtil.close(rs);
|
||||||
|
IoUtil.close(stmt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static Connection getConnection(String driver, String url, String username, String password) {
|
public static Connection getConnection(String driver, String url, String username, String password) {
|
||||||
Connection conn = null;
|
Connection conn = null;
|
||||||
try {
|
try {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||||
com.qiaoba.common.database.factories.DynamicDataSourceFactory,\
|
com.qiaoba.common.database.factories.DynamicDataSourceFactory,\
|
||||||
com.qiaoba.common.database.monitor.DatasourceConnectionMonitor,\
|
com.qiaoba.common.database.monitor.OnlineDatasourceMonitor,\
|
||||||
|
com.qiaoba.common.database.monitor.NotOnlineDatasourceMonitor,\
|
||||||
com.qiaoba.common.database.handlers.schema.SchemaHandlerFactory,\
|
com.qiaoba.common.database.handlers.schema.SchemaHandlerFactory,\
|
||||||
com.qiaoba.common.database.handlers.schema.MysqlSchemaHandler,\
|
com.qiaoba.common.database.handlers.schema.MysqlSchemaHandler,\
|
||||||
com.qiaoba.common.database.config.MybatisPlusConfig
|
com.qiaoba.common.database.config.MybatisPlusConfig
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
<module>qiaoba-module-monitor</module>
|
<module>qiaoba-module-monitor</module>
|
||||||
<module>qiaoba-module-tenant</module>
|
<module>qiaoba-module-tenant</module>
|
||||||
<module>qiaoba-module-demo</module>
|
<module>qiaoba-module-demo</module>
|
||||||
|
<module>qiaoba-module-generator</module>
|
||||||
</modules>
|
</modules>
|
||||||
|
|
||||||
<description>qiaoba-modules: 新建的模块, 统一放在这个模块下面</description>
|
<description>qiaoba-modules: 新建的模块, 统一放在这个模块下面</description>
|
||||||
|
34
qiaoba-modules/qiaoba-module-generator/pom.xml
Normal file
34
qiaoba-modules/qiaoba-module-generator/pom.xml
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<parent>
|
||||||
|
<artifactId>qiaoba-modules</artifactId>
|
||||||
|
<groupId>com.qiaoba</groupId>
|
||||||
|
<version>1.0</version>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<artifactId>qiaoba-module-generator</artifactId>
|
||||||
|
|
||||||
|
<description> 代码生成 </description>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.qiaoba</groupId>
|
||||||
|
<artifactId>qiaoba-common-base</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.qiaoba</groupId>
|
||||||
|
<artifactId>qiaoba-common-datasource</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.qiaoba</groupId>
|
||||||
|
<artifactId>qiaoba-common-web</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.velocity</groupId>
|
||||||
|
<artifactId>velocity-engine-core</artifactId>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
@ -0,0 +1,20 @@
|
|||||||
|
package com.qiaoba.module.generator.controller;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 代码生成 Web层
|
||||||
|
*
|
||||||
|
* @author ailanyin
|
||||||
|
* @version 1.0
|
||||||
|
* @since 2023/6/26 9:09
|
||||||
|
*/
|
||||||
|
@Validated
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/tool/gen")
|
||||||
|
public class GenController {
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
package com.qiaoba.module.generator.entity.dto;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据库表信息
|
||||||
|
*
|
||||||
|
* @author ailanyin
|
||||||
|
* @version 1.0
|
||||||
|
* @since 2023/6/26 9:21
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class TableDto implements Serializable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 表名称
|
||||||
|
*/
|
||||||
|
private String tableName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 表描述
|
||||||
|
*/
|
||||||
|
private String tableComment;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据库类型 Mysql/Oracle/PgSql/Sql Server
|
||||||
|
*/
|
||||||
|
private String dbType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 模式
|
||||||
|
*/
|
||||||
|
private String schema;
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
package com.qiaoba.module.generator.entity.vo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据库表信息
|
||||||
|
*
|
||||||
|
* @author ailanyin
|
||||||
|
* @version 1.0
|
||||||
|
* @since 2023/6/26 9:21
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class DbTableVo implements Serializable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 表名称
|
||||||
|
*/
|
||||||
|
private String tableName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 表描述
|
||||||
|
*/
|
||||||
|
private String tableComment;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
private Date createTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*/
|
||||||
|
private Date updateTime;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
package com.qiaoba.module.generator.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import com.qiaoba.module.generator.entity.dto.TableDto;
|
||||||
|
import com.qiaoba.module.generator.entity.vo.DbTableVo;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据库表 数据层
|
||||||
|
*
|
||||||
|
* @author ailanyin
|
||||||
|
* @version 1.0
|
||||||
|
* @since 2023/6/26 9:24
|
||||||
|
*/
|
||||||
|
public interface TableMapper {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询数据库表
|
||||||
|
*
|
||||||
|
* @param page 分页
|
||||||
|
* @param dto 查询参数
|
||||||
|
* @return list
|
||||||
|
*/
|
||||||
|
Page<DbTableVo> selectPageDbTableList(Page page, @Param("dto") TableDto dto);
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
package com.qiaoba.module.generator.service;
|
||||||
|
|
||||||
|
import com.qiaoba.common.database.entity.PageQuery;
|
||||||
|
import com.qiaoba.common.database.entity.TableDataInfo;
|
||||||
|
import com.qiaoba.module.generator.entity.dto.TableDto;
|
||||||
|
import com.qiaoba.module.generator.entity.vo.DbTableVo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据库表 服务层
|
||||||
|
*
|
||||||
|
* @author ailanyin
|
||||||
|
* @version 1.0
|
||||||
|
* @since 2023/6/26 9:24
|
||||||
|
*/
|
||||||
|
public interface TableService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查询数据库表
|
||||||
|
*
|
||||||
|
* @param dto 查询条件
|
||||||
|
* @param pageQuery 分页信息
|
||||||
|
* @return list
|
||||||
|
*/
|
||||||
|
TableDataInfo<DbTableVo> selectPageDbTableList(TableDto dto, PageQuery pageQuery);
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
package com.qiaoba.module.generator.service.impl;
|
||||||
|
|
||||||
|
import com.qiaoba.common.base.context.BaseContext;
|
||||||
|
import com.qiaoba.common.base.enums.DataBaseEnum;
|
||||||
|
import com.qiaoba.common.database.config.DynamicDataSourceConfig;
|
||||||
|
import com.qiaoba.common.database.entity.PageQuery;
|
||||||
|
import com.qiaoba.common.database.entity.TableDataInfo;
|
||||||
|
import com.qiaoba.module.generator.entity.dto.TableDto;
|
||||||
|
import com.qiaoba.module.generator.entity.vo.DbTableVo;
|
||||||
|
import com.qiaoba.module.generator.mapper.TableMapper;
|
||||||
|
import com.qiaoba.module.generator.service.TableService;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据库表 服务层实现
|
||||||
|
*
|
||||||
|
* @author ailanyin
|
||||||
|
* @version 1.0
|
||||||
|
* @since 2023/6/26 10:12
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class TableServiceImpl implements TableService {
|
||||||
|
|
||||||
|
private final TableMapper tableMapper;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TableDataInfo<DbTableVo> selectPageDbTableList(TableDto dto, PageQuery pageQuery) {
|
||||||
|
String dbType = DynamicDataSourceConfig.TENANT_DATASOURCE_TYPE_MAP.get(BaseContext.getTenantId());
|
||||||
|
dto.setDbType(dbType);
|
||||||
|
dto.setSchema(selectSchema());
|
||||||
|
return TableDataInfo.build(tableMapper.selectPageDbTableList(pageQuery.build(), dto));
|
||||||
|
}
|
||||||
|
|
||||||
|
private String selectSchema() {
|
||||||
|
// PgSQL 需要设置 schema
|
||||||
|
//DataBaseEnum.POSTGRE_SQL.getType().equals(dbType)
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,71 @@
|
|||||||
|
<?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.qiaoba.module.generator.mapper.TableMapper">
|
||||||
|
|
||||||
|
<resultMap type="com.qiaoba.module.generator.entity.vo.DbTableVo" id="DbTableVoResult">
|
||||||
|
<result property="tableName" column="table_name"/>
|
||||||
|
<result property="tableComment" column="table_comment"/>
|
||||||
|
<result property="createTime" column="create_time"/>
|
||||||
|
<result property="updateTime" column="update_time"/>
|
||||||
|
</resultMap>
|
||||||
|
|
||||||
|
<select id="selectPageDbTableList" resultMap="DbTableVoResult">
|
||||||
|
<if test="dto.dbType=='MySQL'.toString()">
|
||||||
|
select table_name, table_comment, create_time, update_time from information_schema.tables
|
||||||
|
where table_schema = (select database())
|
||||||
|
AND table_name NOT LIKE 'qrtz_%' AND table_name NOT LIKE 'gen_%'
|
||||||
|
AND table_name NOT IN (select table_name from gen_table)
|
||||||
|
<if test="dto.tableName != null and dto.tableName != ''">
|
||||||
|
AND lower(table_name) like lower(concat('%', #{dto.tableName}, '%'))
|
||||||
|
</if>
|
||||||
|
<if test="dto.tableComment != null and dto.tableComment != ''">
|
||||||
|
AND lower(table_comment) like lower(concat('%', #{dto.tableComment}, '%'))
|
||||||
|
</if>
|
||||||
|
order by create_time desc
|
||||||
|
</if>
|
||||||
|
|
||||||
|
<if test="dto.dbType=='Oracle'.toString()">
|
||||||
|
select lower(dt.table_name) as table_name, dtc.comments as table_comment, uo.created as create_time, uo.last_ddl_time as update_time
|
||||||
|
from user_tables dt, user_tab_comments dtc, user_objects uo
|
||||||
|
where dt.table_name = dtc.table_name
|
||||||
|
and dt.table_name = uo.object_name
|
||||||
|
and uo.object_type = 'TABLE'
|
||||||
|
AND dt.table_name NOT LIKE 'XXL_JOB_%' AND dt.table_name NOT LIKE 'GEN_%'
|
||||||
|
AND dt.table_name NOT IN (select table_name from gen_table)
|
||||||
|
<if test="dto.tableName != null and dto.tableName != ''">
|
||||||
|
AND lower(dt.table_name) like lower(concat(concat('%', #{dto.tableName}), '%'))
|
||||||
|
</if>
|
||||||
|
<if test="dto.tableComment != null and dto.tableComment != ''">
|
||||||
|
AND lower(dtc.comments) like lower(concat(concat('%', #{dto.tableComment}), '%'))
|
||||||
|
</if>
|
||||||
|
order by create_time desc
|
||||||
|
</if>
|
||||||
|
|
||||||
|
<if test="dto.dbType=='PostgreSQL'.toString()">
|
||||||
|
select table_name, table_comment, create_time, update_time
|
||||||
|
from (
|
||||||
|
SELECT c.relname AS table_name,
|
||||||
|
obj_description(c.oid) AS table_comment,
|
||||||
|
CURRENT_TIMESTAMP AS create_time,
|
||||||
|
CURRENT_TIMESTAMP AS update_time
|
||||||
|
FROM pg_class c
|
||||||
|
LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
|
||||||
|
WHERE (c.relkind = ANY (ARRAY ['r'::"char", 'p'::"char"]))
|
||||||
|
AND c.relname != 'spatial_%'::text
|
||||||
|
AND n.nspname = #{dto.schema}::name
|
||||||
|
AND n.nspname <![CDATA[ <> ]]> ''::name
|
||||||
|
) list_table
|
||||||
|
where table_name NOT LIKE 'xxl_job_%' AND table_name NOT LIKE 'gen_%'
|
||||||
|
AND table_name NOT IN (select table_name from gen_table)
|
||||||
|
<if test="dto.tableName != null and dto.tableName != ''">
|
||||||
|
AND lower(table_name) like lower(concat('%', #{dto.tableName}, '%'))
|
||||||
|
</if>
|
||||||
|
<if test="dto.tableComment != null and dto.tableComment != ''">
|
||||||
|
AND lower(table_comment) like lower(concat('%', #{dto.tableComment}, '%'))
|
||||||
|
</if>
|
||||||
|
order by create_time desc
|
||||||
|
</if>
|
||||||
|
</select>
|
||||||
|
</mapper>
|
@ -24,8 +24,16 @@ public class SysTenantDatasourceParam implements Serializable {
|
|||||||
|
|
||||||
private String isPrimary;
|
private String isPrimary;
|
||||||
|
|
||||||
|
private String ip;
|
||||||
|
|
||||||
public SysTenantDatasourceParam(String tenantId) {
|
public SysTenantDatasourceParam(String tenantId) {
|
||||||
this.tenantId = tenantId;
|
this.tenantId = tenantId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public SysTenantDatasourceParam(String tenantId, String isPrimary) {
|
||||||
|
this.tenantId = tenantId;
|
||||||
|
this.isPrimary = isPrimary;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,15 @@ public interface SysTenantDatasourceService {
|
|||||||
*/
|
*/
|
||||||
SysTenantDatasource selectPrimary(String tenantId);
|
SysTenantDatasource selectPrimary(String tenantId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过IP查询
|
||||||
|
*
|
||||||
|
* @param tenantId tenantId
|
||||||
|
* @param ip ip
|
||||||
|
* @return obj
|
||||||
|
*/
|
||||||
|
SysTenantDatasource selectByIp(String tenantId, String ip);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 新增
|
* 新增
|
||||||
*
|
*
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
package com.qiaoba.module.tenant.service.impl;
|
package com.qiaoba.module.tenant.service.impl;
|
||||||
|
|
||||||
import cn.hutool.core.bean.BeanUtil;
|
import cn.hutool.core.bean.BeanUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
import com.qiaoba.common.base.enums.BaseEnum;
|
import com.qiaoba.common.base.enums.BaseEnum;
|
||||||
import com.qiaoba.common.base.enums.DataBaseEnum;
|
import com.qiaoba.common.base.enums.DataBaseEnum;
|
||||||
|
import com.qiaoba.common.base.exceptions.ServiceException;
|
||||||
import com.qiaoba.common.database.entity.DynamicDataSource;
|
import com.qiaoba.common.database.entity.DynamicDataSource;
|
||||||
import com.qiaoba.common.database.service.DynamicDatasourceService;
|
import com.qiaoba.common.database.service.DynamicDatasourceService;
|
||||||
import com.qiaoba.module.tenant.entity.SysTenantDatasource;
|
import com.qiaoba.module.tenant.entity.SysTenantDatasource;
|
||||||
@ -11,10 +13,7 @@ import com.qiaoba.module.tenant.service.SysTenantDatasourceService;
|
|||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 动态数据源接口
|
* 动态数据源接口
|
||||||
@ -35,7 +34,7 @@ public class DynamicDatasourceServiceImpl implements DynamicDatasourceService {
|
|||||||
List<String> tenantIds = sysTenantDatasourceService.selectTenantIds();
|
List<String> tenantIds = sysTenantDatasourceService.selectTenantIds();
|
||||||
for (String tenantId : tenantIds) {
|
for (String tenantId : tenantIds) {
|
||||||
List<SysTenantDatasource> datasourceList = sysTenantDatasourceService.selectList(new SysTenantDatasourceParam(tenantId));
|
List<SysTenantDatasource> datasourceList = sysTenantDatasourceService.selectList(new SysTenantDatasourceParam(tenantId));
|
||||||
datasourceMap.put(tenantId, transform(datasourceList));
|
datasourceMap.put(tenantId, transformList(datasourceList));
|
||||||
}
|
}
|
||||||
return datasourceMap;
|
return datasourceMap;
|
||||||
}
|
}
|
||||||
@ -51,17 +50,30 @@ public class DynamicDatasourceServiceImpl implements DynamicDatasourceService {
|
|||||||
sysTenantDatasourceService.setBackupDatasourceExcludeId(tenantId, datasourceId);
|
sysTenantDatasourceService.setBackupDatasourceExcludeId(tenantId, datasourceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<DynamicDataSource> transform(List<SysTenantDatasource> datasourceList) {
|
@Override
|
||||||
|
public DynamicDataSource selectByIp(String tenantId, String ip) {
|
||||||
|
SysTenantDatasource sysTenantDatasource = sysTenantDatasourceService.selectByIp(tenantId, ip);
|
||||||
|
if (Objects.isNull(sysTenantDatasource)) {
|
||||||
|
throw new ServiceException(StrUtil.format("未找到数据源,查询方式: {}", ip));
|
||||||
|
}
|
||||||
|
return transform(sysTenantDatasource);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<DynamicDataSource> transformList(List<SysTenantDatasource> datasourceList) {
|
||||||
List<DynamicDataSource> dynamicDataSourceList = new ArrayList<>();
|
List<DynamicDataSource> dynamicDataSourceList = new ArrayList<>();
|
||||||
for (SysTenantDatasource datasource : datasourceList) {
|
for (SysTenantDatasource datasource : datasourceList) {
|
||||||
DynamicDataSource dynamicDataSource = BeanUtil.copyProperties(datasource, DynamicDataSource.class);
|
dynamicDataSourceList.add(transform(datasource));
|
||||||
dynamicDataSource.setInitialSize(datasource.getInitCount());
|
|
||||||
dynamicDataSource.setMinIdle(datasource.getMinCount());
|
|
||||||
dynamicDataSource.setMaxActive(datasource.getMaxCount());
|
|
||||||
dynamicDataSource.setDriver(DataBaseEnum.getDriver(datasource.getType()));
|
|
||||||
dynamicDataSource.setUrl(DataBaseEnum.getUrl(datasource.getType(), datasource.getIp(), datasource.getPort(), datasource.getName()));
|
|
||||||
dynamicDataSourceList.add(dynamicDataSource);
|
|
||||||
}
|
}
|
||||||
return dynamicDataSourceList;
|
return dynamicDataSourceList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private DynamicDataSource transform(SysTenantDatasource datasource) {
|
||||||
|
DynamicDataSource dynamicDataSource = BeanUtil.copyProperties(datasource, DynamicDataSource.class);
|
||||||
|
dynamicDataSource.setInitialSize(datasource.getInitCount());
|
||||||
|
dynamicDataSource.setMinIdle(datasource.getMinCount());
|
||||||
|
dynamicDataSource.setMaxActive(datasource.getMaxCount());
|
||||||
|
dynamicDataSource.setDriver(DataBaseEnum.getDriver(datasource.getType()));
|
||||||
|
dynamicDataSource.setUrl(DataBaseEnum.getUrl(datasource.getType(), datasource.getIp(), datasource.getPort(), datasource.getName()));
|
||||||
|
return dynamicDataSource;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,13 @@ public class SysTenantDatasourceServiceImpl implements SysTenantDatasourceServic
|
|||||||
return sysTenantDatasourceMapper.selectOne(paramToWrapper(new SysTenantDatasourceParam(tenantId, BaseEnum.YES.getCode())));
|
return sysTenantDatasourceMapper.selectOne(paramToWrapper(new SysTenantDatasourceParam(tenantId, BaseEnum.YES.getCode())));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SysTenantDatasource selectByIp(String tenantId, String ip) {
|
||||||
|
SysTenantDatasourceParam param = new SysTenantDatasourceParam(tenantId);
|
||||||
|
param.setIp(ip);
|
||||||
|
return sysTenantDatasourceMapper.selectOne(paramToWrapper(param));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int insert(SysTenantDatasource sysTenantDatasource) {
|
public int insert(SysTenantDatasource sysTenantDatasource) {
|
||||||
return sysTenantDatasourceMapper.insert(sysTenantDatasource);
|
return sysTenantDatasourceMapper.insert(sysTenantDatasource);
|
||||||
@ -69,6 +76,7 @@ public class SysTenantDatasourceServiceImpl implements SysTenantDatasourceServic
|
|||||||
QueryWrapper<SysTenantDatasource> wrapper = new QueryWrapper<>();
|
QueryWrapper<SysTenantDatasource> wrapper = new QueryWrapper<>();
|
||||||
wrapper.lambda()
|
wrapper.lambda()
|
||||||
.eq(StrUtil.isNotBlank(param.getTenantId()), SysTenantDatasource::getTenantId, param.getTenantId())
|
.eq(StrUtil.isNotBlank(param.getTenantId()), SysTenantDatasource::getTenantId, param.getTenantId())
|
||||||
|
.eq(StrUtil.isNotBlank(param.getIp()), SysTenantDatasource::getIp, param.getIp())
|
||||||
.eq(StrUtil.isNotBlank(param.getIsPrimary()), SysTenantDatasource::getIsPrimary, param.getIsPrimary());
|
.eq(StrUtil.isNotBlank(param.getIsPrimary()), SysTenantDatasource::getIsPrimary, param.getIsPrimary());
|
||||||
return wrapper;
|
return wrapper;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user