add
This commit is contained in:
@ -11,7 +11,7 @@
|
|||||||
Target Server Version : 50742
|
Target Server Version : 50742
|
||||||
File Encoding : 65001
|
File Encoding : 65001
|
||||||
|
|
||||||
Date: 13/06/2023 23:11:45
|
Date: 14/06/2023 16:41:43
|
||||||
*/
|
*/
|
||||||
|
|
||||||
SET NAMES utf8mb4;
|
SET NAMES utf8mb4;
|
||||||
@ -404,7 +404,7 @@ INSERT INTO `sys_tenant` VALUES (1668531266220535809, '而且其', NULL, NULL, N
|
|||||||
-- ----------------------------
|
-- ----------------------------
|
||||||
DROP TABLE IF EXISTS `sys_tenant_datasource`;
|
DROP TABLE IF EXISTS `sys_tenant_datasource`;
|
||||||
CREATE TABLE `sys_tenant_datasource` (
|
CREATE TABLE `sys_tenant_datasource` (
|
||||||
`id` bigint(20) NOT NULL,
|
`datasource_id` bigint(20) NOT NULL,
|
||||||
`type` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '类型',
|
`type` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '类型',
|
||||||
`ip` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'IP',
|
`ip` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'IP',
|
||||||
`port` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '端口',
|
`port` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '端口',
|
||||||
@ -421,14 +421,15 @@ CREATE TABLE `sys_tenant_datasource` (
|
|||||||
`update_time` datetime NULL DEFAULT NULL COMMENT '更新时间',
|
`update_time` datetime NULL DEFAULT NULL COMMENT '更新时间',
|
||||||
`remark` varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '备注',
|
`remark` varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '备注',
|
||||||
`tenant_id` bigint(20) NOT NULL,
|
`tenant_id` bigint(20) NOT NULL,
|
||||||
PRIMARY KEY (`id`) USING BTREE
|
PRIMARY KEY (`datasource_id`) USING BTREE
|
||||||
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '租户数据源' ROW_FORMAT = DYNAMIC;
|
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '租户数据源' ROW_FORMAT = DYNAMIC;
|
||||||
|
|
||||||
-- ----------------------------
|
-- ----------------------------
|
||||||
-- Records of sys_tenant_datasource
|
-- Records of sys_tenant_datasource
|
||||||
-- ----------------------------
|
-- ----------------------------
|
||||||
INSERT INTO `sys_tenant_datasource` VALUES (166160396877907999, 'MySQL', '192.168.0.203', '3306', 'qiaoba-boot', 'root', 'Root123456789.', 5, 10, 20, '1', 'admin', '2023-06-07 14:36:11', '', NULL, NULL, 2);
|
INSERT INTO `sys_tenant_datasource` VALUES (166160396877907998, 'MySQL', '192.168.0.202', '3306', 'qiaoba-boot', 'root', 'Root123456789.', 5, 10, 20, '1', 'admin', '2023-06-07 14:36:11', '', NULL, NULL, 2);
|
||||||
INSERT INTO `sys_tenant_datasource` VALUES (166160396877908000, 'MySQL', '120.79.217.22', '3306', 'test1234111', 'root', 'FeyZ7xZr6JtuKibm\n', 5, 10, 20, '1', 'admin', '2023-06-07 14:36:11', '', NULL, NULL, 2);
|
INSERT INTO `sys_tenant_datasource` VALUES (166160396877907999, 'MySQL', '192.168.0.203', '3306', 'qiaoba-boot', 'root', 'Root123456789.', 5, 10, 20, '0', 'admin', '2023-06-07 14:36:11', '', NULL, NULL, 2);
|
||||||
|
INSERT INTO `sys_tenant_datasource` VALUES (166160396877908000, 'MySQL', '127.0.0.1', '3306', 'qiaoba-boot', 'root', 'root', 5, 10, 20, '0', 'admin', '2023-06-07 14:36:11', '', NULL, NULL, 2);
|
||||||
|
|
||||||
-- ----------------------------
|
-- ----------------------------
|
||||||
-- Table structure for sys_user
|
-- Table structure for sys_user
|
||||||
|
@ -2,9 +2,9 @@ qiaoba:
|
|||||||
file-upload-path: C:/${spring.application.name}/uploadPath/
|
file-upload-path: C:/${spring.application.name}/uploadPath/
|
||||||
dataSources:
|
dataSources:
|
||||||
- driver: com.mysql.cj.jdbc.Driver
|
- driver: com.mysql.cj.jdbc.Driver
|
||||||
url: jdbc:mysql://192.168.0.202:3306/${spring.application.name}?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true&allowMultiQueries=true
|
url: jdbc:mysql://127.0.0.1:3306/${spring.application.name}?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true&allowMultiQueries=true
|
||||||
username: root
|
username: root
|
||||||
password: Root123456789.
|
password: root
|
||||||
#连接池初始化大小
|
#连接池初始化大小
|
||||||
initial-size: 5
|
initial-size: 5
|
||||||
#最小空闲线程数
|
#最小空闲线程数
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package com.qiaoba.common.database.config;
|
package com.qiaoba.common.database.config;
|
||||||
|
|
||||||
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.collection.CollUtil;
|
||||||
|
import cn.hutool.core.collection.ListUtil;
|
||||||
import com.alibaba.druid.pool.DruidDataSource;
|
import com.alibaba.druid.pool.DruidDataSource;
|
||||||
import com.qiaoba.common.base.constants.TenantConstant;
|
import com.qiaoba.common.base.constants.TenantConstant;
|
||||||
import com.qiaoba.common.base.context.BaseContext;
|
import com.qiaoba.common.base.context.BaseContext;
|
||||||
@ -9,13 +10,13 @@ import com.qiaoba.common.database.constants.DynamicDatasourceConstant;
|
|||||||
import com.qiaoba.common.database.entity.DynamicDataSource;
|
import com.qiaoba.common.database.entity.DynamicDataSource;
|
||||||
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 lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
import javax.annotation.PostConstruct;
|
||||||
import javax.annotation.PreDestroy;
|
import javax.annotation.PreDestroy;
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
@ -43,6 +44,11 @@ public class DynamicDataSourceConfig {
|
|||||||
@Resource
|
@Resource
|
||||||
private DynamicDatasourceService dynamicDatasourceService;
|
private DynamicDatasourceService dynamicDatasourceService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据源加载完成
|
||||||
|
*/
|
||||||
|
public static Boolean COMPLETE_LOAD_DATASOURCE = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 主要数据源
|
* 主要数据源
|
||||||
*/
|
*/
|
||||||
@ -53,6 +59,11 @@ public class DynamicDataSourceConfig {
|
|||||||
*/
|
*/
|
||||||
public static Map<String, List<DynamicDataSource>> BACKUP_DATASOURCE_MAP = new ConcurrentHashMap<>();
|
public static Map<String, List<DynamicDataSource>> BACKUP_DATASOURCE_MAP = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 租户 ids
|
||||||
|
*/
|
||||||
|
public static List<String> TENANT_IDS = ListUtil.toList(TenantConstant.DEFAULT_TENANT_ID);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 把DynamicDataSourceContext 纳入容器管理,其他地方使用DynamicDataSourceConfig 类可以直接从容器取对象,并调用freshDataSource方法
|
* 把DynamicDataSourceContext 纳入容器管理,其他地方使用DynamicDataSourceConfig 类可以直接从容器取对象,并调用freshDataSource方法
|
||||||
*/
|
*/
|
||||||
@ -62,6 +73,7 @@ public class DynamicDataSourceConfig {
|
|||||||
initDefault();
|
initDefault();
|
||||||
// 加载租户数据源
|
// 加载租户数据源
|
||||||
initTenant();
|
initTenant();
|
||||||
|
COMPLETE_LOAD_DATASOURCE = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static DruidDataSource getPrimaryDataSource(String tenantId) {
|
public static DruidDataSource getPrimaryDataSource(String tenantId) {
|
||||||
@ -90,14 +102,17 @@ public class DynamicDataSourceConfig {
|
|||||||
addPrimaryMap(DynamicDatasourceConstant.MASTER_PREFIX + tenantId, buildDataSource(dataSources.get(i)));
|
addPrimaryMap(DynamicDatasourceConstant.MASTER_PREFIX + tenantId, buildDataSource(dataSources.get(i)));
|
||||||
// 去除主要数据源,剩下皆为备用数据源
|
// 去除主要数据源,剩下皆为备用数据源
|
||||||
dataSources.remove(i);
|
dataSources.remove(i);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 备用数据源
|
// 备用数据源
|
||||||
addBackupMap(tenantId, dataSources);
|
addBackupMap(tenantId, dataSources);
|
||||||
|
TENANT_IDS.add(tenantId);
|
||||||
}
|
}
|
||||||
BaseContext.clearAllHolder();
|
BaseContext.clearAllHolder();
|
||||||
// 刷新数据源
|
// 刷新数据源
|
||||||
dataSource.freshDataSource(PRIMARY_DATASOURCE_MAP);
|
dataSource.freshDataSource(PRIMARY_DATASOURCE_MAP);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void deleteTenantDataSource(String tenantId) {
|
public void deleteTenantDataSource(String tenantId) {
|
||||||
@ -105,7 +120,18 @@ public class DynamicDataSourceConfig {
|
|||||||
dataSource.freshDataSource(PRIMARY_DATASOURCE_MAP);
|
dataSource.freshDataSource(PRIMARY_DATASOURCE_MAP);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Object buildDataSource(DynamicDataSource dynamicDataSource) {
|
public void changePrimaryDatasource(String tenantId, Object datasource) {
|
||||||
|
PRIMARY_DATASOURCE_MAP.put(DynamicDatasourceConstant.MASTER_PREFIX + tenantId, datasource);
|
||||||
|
dataSource.freshDataSource(PRIMARY_DATASOURCE_MAP);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Object buildDataSource(DynamicDataSource dynamicDataSource) {
|
||||||
|
log.debug("正在创建数据源DataSource, 租户: {}", dynamicDataSource.getTenantId());
|
||||||
|
boolean connect = JdbcUtil.checkConnect(dynamicDataSource.getDriver(), dynamicDataSource.getUrl(), dynamicDataSource.getUsername(), dynamicDataSource.getPassword());
|
||||||
|
if (!connect) {
|
||||||
|
log.error("租户: {} 数据源连接失败, Url: {}", dynamicDataSource.getTenantId(), dynamicDataSource.getUrl());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
DruidDataSource dataSource = new DruidDataSource();
|
DruidDataSource dataSource = new DruidDataSource();
|
||||||
dataSource.setUrl(dynamicDataSource.getUrl());
|
dataSource.setUrl(dynamicDataSource.getUrl());
|
||||||
dataSource.setUsername(dynamicDataSource.getUsername());
|
dataSource.setUsername(dynamicDataSource.getUsername());
|
||||||
@ -114,10 +140,11 @@ public class DynamicDataSourceConfig {
|
|||||||
dataSource.setInitialSize(dynamicDataSource.getInitialSize());
|
dataSource.setInitialSize(dynamicDataSource.getInitialSize());
|
||||||
dataSource.setMinIdle(dynamicDataSource.getMinIdle());
|
dataSource.setMinIdle(dynamicDataSource.getMinIdle());
|
||||||
dataSource.setMaxActive(dynamicDataSource.getMaxActive());
|
dataSource.setMaxActive(dynamicDataSource.getMaxActive());
|
||||||
|
dataSource.setKeepAlive(false);
|
||||||
try {
|
try {
|
||||||
dataSource.init();
|
dataSource.init();
|
||||||
return dataSource;
|
return dataSource;
|
||||||
} catch (SQLException e) {
|
} catch (Exception e) {
|
||||||
dataSource.close();
|
dataSource.close();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -131,7 +158,9 @@ public class DynamicDataSourceConfig {
|
|||||||
Set<Map.Entry<Object, Object>> entries = PRIMARY_DATASOURCE_MAP.entrySet();
|
Set<Map.Entry<Object, Object>> entries = PRIMARY_DATASOURCE_MAP.entrySet();
|
||||||
for (Map.Entry<Object, Object> entry : entries) {
|
for (Map.Entry<Object, Object> entry : entries) {
|
||||||
DruidDataSource dataSource = (DruidDataSource) entry.getValue();
|
DruidDataSource dataSource = (DruidDataSource) entry.getValue();
|
||||||
dataSource.close();
|
if (!dataSource.isClosed()) {
|
||||||
|
dataSource.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,9 +19,9 @@ import lombok.NoArgsConstructor;
|
|||||||
public class DynamicDataSource {
|
public class DynamicDataSource {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 租户Code
|
* 数据源ID
|
||||||
*/
|
*/
|
||||||
private String tenantId;
|
private String datasourceId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 主要数据源
|
* 主要数据源
|
||||||
@ -63,4 +63,8 @@ public class DynamicDataSource {
|
|||||||
*/
|
*/
|
||||||
private Integer maxActive;
|
private Integer maxActive;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 租户ID
|
||||||
|
*/
|
||||||
|
private String tenantId;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package com.qiaoba.common.database.interceptors;
|
package com.qiaoba.common.database.interceptors;
|
||||||
|
|
||||||
|
import cn.hutool.core.io.IoUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
|
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
|
||||||
import com.qiaoba.common.base.context.BaseContext;
|
import com.qiaoba.common.base.context.BaseContext;
|
||||||
@ -8,6 +9,7 @@ import org.springframework.beans.factory.annotation.Value;
|
|||||||
|
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
import java.sql.Statement;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -28,10 +30,14 @@ public class SchemaInterceptor implements InnerInterceptor {
|
|||||||
if (Objects.nonNull(BaseContext.isSchemaMode()) && BaseContext.isSchemaMode()) {
|
if (Objects.nonNull(BaseContext.isSchemaMode()) && BaseContext.isSchemaMode()) {
|
||||||
// eg: use qiaoba-1;
|
// eg: use qiaoba-1;
|
||||||
String sql = StrUtil.format("use `{}-{}`;", baseDatabase, BaseContext.getTenantId());
|
String sql = StrUtil.format("use `{}-{}`;", baseDatabase, BaseContext.getTenantId());
|
||||||
|
Statement statement = null;
|
||||||
try {
|
try {
|
||||||
conn.createStatement().execute(sql);
|
statement = conn.createStatement();
|
||||||
|
statement.execute(sql);
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
|
} finally {
|
||||||
|
IoUtil.close(statement);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
InnerInterceptor.super.beforePrepare(sh, conn, transactionTimeout);
|
InnerInterceptor.super.beforePrepare(sh, conn, transactionTimeout);
|
||||||
|
@ -0,0 +1,137 @@
|
|||||||
|
package com.qiaoba.common.database.monitor;
|
||||||
|
|
||||||
|
import com.alibaba.druid.pool.DruidDataSource;
|
||||||
|
import com.alibaba.druid.util.JdbcUtils;
|
||||||
|
import com.qiaoba.common.base.constants.TenantConstant;
|
||||||
|
import com.qiaoba.common.database.config.DynamicDataSourceConfig;
|
||||||
|
import com.qiaoba.common.database.constants.DynamicDatasourceConstant;
|
||||||
|
import com.qiaoba.common.database.entity.DynamicDataSource;
|
||||||
|
import com.qiaoba.common.database.service.DynamicDatasourceService;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import javax.annotation.PostConstruct;
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import java.sql.Connection;
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.sql.Statement;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 监控-数据源连接监控
|
||||||
|
*
|
||||||
|
* @author ailanyin
|
||||||
|
* @version 1.0
|
||||||
|
* @since 2023/6/14 14:48
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
@Slf4j
|
||||||
|
public class DatasourceConnectionMonitor {
|
||||||
|
|
||||||
|
private static final String CHECK_SQL = "SELECT 1";
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private DynamicDataSourceConfig dynamicDataSourceConfig;
|
||||||
|
@Resource
|
||||||
|
private DynamicDatasourceService dynamicDatasourceService;
|
||||||
|
|
||||||
|
//@PostConstruct
|
||||||
|
public void init() {
|
||||||
|
new Timer().schedule(new TimerTask() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
// 项目加载数据源还未完成
|
||||||
|
if (!DynamicDataSourceConfig.COMPLETE_LOAD_DATASOURCE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
log.debug("开始运行数据源监控线程, 时间: {}", new Date());
|
||||||
|
for (String tenantId : DynamicDataSourceConfig.TENANT_IDS) {
|
||||||
|
Object primary = DynamicDataSourceConfig.PRIMARY_DATASOURCE_MAP.get(DynamicDatasourceConstant.MASTER_PREFIX + tenantId);
|
||||||
|
if (Objects.isNull(primary)) {
|
||||||
|
// 说明初始化主要数据源的时候出错
|
||||||
|
log.error("租户[{}]-目前主数据源异常, 开始切换备用数据源", tenantId);
|
||||||
|
// 切换备用数据源
|
||||||
|
changePrimary(tenantId);
|
||||||
|
} else {
|
||||||
|
DruidDataSource dataSource = (DruidDataSource) primary;
|
||||||
|
try {
|
||||||
|
Connection connection = dataSource.getConnection();
|
||||||
|
if (check(connection)) {
|
||||||
|
// 说明数据源正常
|
||||||
|
log.debug("租户[{}]-目前主数据源正常, 无需切换数据源", tenantId);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
log.error("租户[{}]-目前主数据源异常, 开始切换备用数据源", tenantId);
|
||||||
|
// 关闭原有异常数据源
|
||||||
|
connection.close();
|
||||||
|
dataSource.close();
|
||||||
|
// 主数据源异常 切换备用数据源
|
||||||
|
changePrimary(tenantId);
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.debug("结束运行数据源监控线程, 时间: {}", new Date());
|
||||||
|
}
|
||||||
|
}, 0, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Boolean check(Connection conn) {
|
||||||
|
Statement stmt = null;
|
||||||
|
ResultSet rs = null;
|
||||||
|
try {
|
||||||
|
stmt = conn.createStatement();
|
||||||
|
stmt.setQueryTimeout(1);
|
||||||
|
rs = stmt.executeQuery(CHECK_SQL);
|
||||||
|
return true;
|
||||||
|
} catch (Exception e) {
|
||||||
|
return false;
|
||||||
|
} finally {
|
||||||
|
JdbcUtils.close(rs);
|
||||||
|
JdbcUtils.close(stmt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 切换主数据源
|
||||||
|
*
|
||||||
|
* @param tenantId 租户ID
|
||||||
|
*/
|
||||||
|
private void changePrimary(String tenantId) {
|
||||||
|
// 备用数据源
|
||||||
|
List<DynamicDataSource> dataSources = DynamicDataSourceConfig.BACKUP_DATASOURCE_MAP.get(tenantId);
|
||||||
|
if (Objects.isNull(dataSources)) {
|
||||||
|
log.error("租户:[{}]切换备用数据源失败, 原因: 没有备用数据源", tenantId);
|
||||||
|
}
|
||||||
|
|
||||||
|
Integer backupIndex = null;
|
||||||
|
|
||||||
|
for (int i = 0; i < dataSources.size(); i++) {
|
||||||
|
Object dynamicDataSource = DynamicDataSourceConfig.buildDataSource(dataSources.get(i));
|
||||||
|
// 不是空,说明备用数据源有用
|
||||||
|
if (Objects.nonNull(dynamicDataSource)) {
|
||||||
|
dynamicDataSourceConfig.changePrimaryDatasource(tenantId, dynamicDataSource);
|
||||||
|
backupIndex = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Objects.nonNull(backupIndex)) {
|
||||||
|
// 切换成功
|
||||||
|
DynamicDataSource dynamicDataSource = dataSources.get(backupIndex);
|
||||||
|
// 更改数据库中该数据源为主要数据源
|
||||||
|
if (!TenantConstant.DEFAULT_TENANT_ID.equals(dynamicDataSource.getTenantId())) {
|
||||||
|
dynamicDatasourceService.changePrimaryDatasource(dynamicDataSource.getTenantId(), dynamicDataSource.getDatasourceId());
|
||||||
|
}
|
||||||
|
// 备用数据源集合删除该数据源
|
||||||
|
dataSources.remove((int) backupIndex);
|
||||||
|
log.info("租户:[{}]切换备用数据源成功, 现主数据ID: {}", tenantId, dynamicDataSource.getDatasourceId());
|
||||||
|
} else {
|
||||||
|
log.error("租户:[{}]切换备用数据源失败, 原因: 备用数据源均无效", tenantId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -20,4 +20,12 @@ public interface DynamicDatasourceService {
|
|||||||
* @return 数据源集合 key: tenantId | value: list
|
* @return 数据源集合 key: tenantId | value: list
|
||||||
*/
|
*/
|
||||||
Map<String, List<DynamicDataSource>> loadAllTenantDatasource();
|
Map<String, List<DynamicDataSource>> loadAllTenantDatasource();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 改变主数据源
|
||||||
|
*
|
||||||
|
* @param tenantId 租户ID
|
||||||
|
* @param datasourceId 数据源ID
|
||||||
|
*/
|
||||||
|
void changePrimaryDatasource(String tenantId, String datasourceId);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package com.qiaoba.common.database.utils;
|
package com.qiaoba.common.database.utils;
|
||||||
|
|
||||||
|
import cn.hutool.core.io.IoUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import com.qiaoba.common.base.exceptions.ServiceException;
|
import com.qiaoba.common.base.exceptions.ServiceException;
|
||||||
|
|
||||||
@ -39,13 +40,7 @@ public class JdbcUtil {
|
|||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
return false;
|
return false;
|
||||||
} finally {
|
} finally {
|
||||||
if (conn != null) {
|
IoUtil.close(conn);
|
||||||
try {
|
|
||||||
conn.close();
|
|
||||||
} catch (SQLException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
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.config.MybatisPlusConfig
|
com.qiaoba.common.database.config.MybatisPlusConfig
|
||||||
|
@ -27,7 +27,7 @@ public class SysTenantDatasource extends BaseEntity {
|
|||||||
|
|
||||||
@TableId
|
@TableId
|
||||||
@NotNull(message = "id不能为空", groups = {EditGroup.class})
|
@NotNull(message = "id不能为空", groups = {EditGroup.class})
|
||||||
private String id;
|
private String datasourceId;
|
||||||
|
|
||||||
@NotBlank(message = "数据源类型不能为空", groups = {AddGroup.class, EditGroup.class})
|
@NotBlank(message = "数据源类型不能为空", groups = {AddGroup.class, EditGroup.class})
|
||||||
@Schema(description = "数据源类型")
|
@Schema(description = "数据源类型")
|
||||||
|
@ -2,6 +2,7 @@ package com.qiaoba.module.tenant.mapper;
|
|||||||
|
|
||||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
import com.qiaoba.module.tenant.entity.SysTenantDatasource;
|
import com.qiaoba.module.tenant.entity.SysTenantDatasource;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -21,4 +22,12 @@ public interface SysTenantDatasourceMapper extends BaseMapper<SysTenantDatasourc
|
|||||||
*/
|
*/
|
||||||
List<String> selectTenantIds();
|
List<String> selectTenantIds();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将租户下的除datasourceId之外的其他数据源设置为备用
|
||||||
|
*
|
||||||
|
* @param tenantId tenantId
|
||||||
|
* @param excludeId 排除Id
|
||||||
|
* @param code 否
|
||||||
|
*/
|
||||||
|
void setBackupDatasourceExcludeId(@Param("tenantId") String tenantId, @Param("excludeId") String excludeId, @Param("code") String code);
|
||||||
}
|
}
|
||||||
|
@ -68,4 +68,12 @@ public interface SysTenantDatasourceService {
|
|||||||
* @return tenantIds
|
* @return tenantIds
|
||||||
*/
|
*/
|
||||||
List<String> selectTenantIds();
|
List<String> selectTenantIds();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将租户下的除datasourceId之外的其他数据源设置为备用
|
||||||
|
*
|
||||||
|
* @param tenantId tenantId
|
||||||
|
* @param excludeId 排除Id
|
||||||
|
*/
|
||||||
|
void setBackupDatasourceExcludeId(String tenantId, String excludeId);
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,17 @@ public class DynamicDatasourceServiceImpl implements DynamicDatasourceService {
|
|||||||
return datasourceMap;
|
return datasourceMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void changePrimaryDatasource(String tenantId, String datasourceId) {
|
||||||
|
// 将该数据源设置为主要数据源
|
||||||
|
SysTenantDatasource sysTenantDatasource = new SysTenantDatasource();
|
||||||
|
sysTenantDatasource.setDatasourceId(datasourceId);
|
||||||
|
sysTenantDatasource.setIsPrimary(BaseEnum.YES.getCode());
|
||||||
|
sysTenantDatasourceService.updateById(sysTenantDatasource);
|
||||||
|
// 将该租户下的其他数据源设置为非主要数据源
|
||||||
|
sysTenantDatasourceService.setBackupDatasourceExcludeId(tenantId, datasourceId);
|
||||||
|
}
|
||||||
|
|
||||||
private List<DynamicDataSource> transform(List<SysTenantDatasource> datasourceList) {
|
private List<DynamicDataSource> transform(List<SysTenantDatasource> datasourceList) {
|
||||||
List<DynamicDataSource> dynamicDataSourceList = new ArrayList<>();
|
List<DynamicDataSource> dynamicDataSourceList = new ArrayList<>();
|
||||||
for (SysTenantDatasource datasource : datasourceList) {
|
for (SysTenantDatasource datasource : datasourceList) {
|
||||||
|
@ -60,6 +60,11 @@ public class SysTenantDatasourceServiceImpl implements SysTenantDatasourceServic
|
|||||||
return sysTenantDatasourceMapper.selectTenantIds();
|
return sysTenantDatasourceMapper.selectTenantIds();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setBackupDatasourceExcludeId(String tenantId, String excludeId) {
|
||||||
|
sysTenantDatasourceMapper.setBackupDatasourceExcludeId(tenantId,excludeId,BaseEnum.NO.getCode());
|
||||||
|
}
|
||||||
|
|
||||||
private QueryWrapper<SysTenantDatasource> paramToWrapper(SysTenantDatasourceParam param) {
|
private QueryWrapper<SysTenantDatasource> paramToWrapper(SysTenantDatasourceParam param) {
|
||||||
QueryWrapper<SysTenantDatasource> wrapper = new QueryWrapper<>();
|
QueryWrapper<SysTenantDatasource> wrapper = new QueryWrapper<>();
|
||||||
wrapper.lambda()
|
wrapper.lambda()
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package com.qiaoba.module.tenant.utils;
|
package com.qiaoba.module.tenant.utils;
|
||||||
|
|
||||||
import cn.hutool.core.io.FileUtil;
|
import cn.hutool.core.io.FileUtil;
|
||||||
|
import cn.hutool.core.io.IoUtil;
|
||||||
import cn.hutool.core.lang.Snowflake;
|
import cn.hutool.core.lang.Snowflake;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import cn.hutool.db.sql.SqlExecutor;
|
import cn.hutool.db.sql.SqlExecutor;
|
||||||
@ -9,6 +10,8 @@ import org.springframework.core.io.ClassPathResource;
|
|||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.sql.Statement;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -101,14 +104,24 @@ public class MenuUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<String> getAllMenuIdsByTenantId(Connection conn, String tenantId) throws Exception {
|
public static List<String> getAllMenuIdsByTenantId(Connection conn, String tenantId) {
|
||||||
List<String> menuIds = new ArrayList<>();
|
List<String> menuIds = new ArrayList<>();
|
||||||
ResultSet resultSet = conn.createStatement().executeQuery(StrUtil.format("SELECT menu_id FROM `sys_menu` where tenant_id = '{}'", tenantId));
|
ResultSet resultSet = null;
|
||||||
while (resultSet.next()) {
|
Statement statement = null;
|
||||||
String menuId = resultSet.getString("menu_id");
|
try {
|
||||||
if (StrUtil.isNotBlank(menuId)) {
|
statement = conn.createStatement();
|
||||||
menuIds.add(menuId);
|
resultSet = statement.executeQuery(StrUtil.format("SELECT menu_id FROM `sys_menu` where tenant_id = '{}'", tenantId));
|
||||||
|
while (resultSet.next()) {
|
||||||
|
String menuId = resultSet.getString("menu_id");
|
||||||
|
if (StrUtil.isNotBlank(menuId)) {
|
||||||
|
menuIds.add(menuId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} finally {
|
||||||
|
IoUtil.close(resultSet);
|
||||||
|
IoUtil.close(statement);
|
||||||
}
|
}
|
||||||
return menuIds;
|
return menuIds;
|
||||||
}
|
}
|
||||||
|
@ -6,4 +6,9 @@
|
|||||||
<select id="selectTenantIds" resultType="string">
|
<select id="selectTenantIds" resultType="string">
|
||||||
SELECT tenant_id FROM sys_tenant_datasource GROUP BY tenant_id
|
SELECT tenant_id FROM sys_tenant_datasource GROUP BY tenant_id
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
<update id="setBackupDatasourceExcludeId">
|
||||||
|
update sys_tenant_datasource set is_primary = #{code} where tenant_id = #{tenantId} and datasource_id != #{excludeId}
|
||||||
|
</update>
|
||||||
|
|
||||||
</mapper>
|
</mapper>
|
||||||
|
Reference in New Issue
Block a user