add
This commit is contained in:
@ -1,13 +1,13 @@
|
||||
UPDATE sys_dept set tenant_id = '1';
|
||||
UPDATE sys_menu set tenant_id = '1';
|
||||
UPDATE sys_post set tenant_id = '1';
|
||||
UPDATE sys_role set tenant_id = '1';
|
||||
UPDATE sys_user set tenant_id = '1';
|
||||
UPDATE sys_config set tenant_id = '1';
|
||||
UPDATE sys_login_log set tenant_id = '1';
|
||||
UPDATE sys_role_dept set tenant_id = '1';
|
||||
UPDATE sys_role_menu set tenant_id = '1';
|
||||
UPDATE sys_user_post set tenant_id = '1';
|
||||
UPDATE sys_user_role set tenant_id = '1';
|
||||
UPDATE sys_dict_data set tenant_id = '1';
|
||||
UPDATE sys_dict_type set tenant_id = '1';
|
||||
UPDATE sys_dept set tenant_id = '2';
|
||||
UPDATE sys_menu set tenant_id = '2';
|
||||
UPDATE sys_post set tenant_id = '2';
|
||||
UPDATE sys_role set tenant_id = '2';
|
||||
UPDATE sys_user set tenant_id = '2';
|
||||
UPDATE sys_config set tenant_id = '2';
|
||||
UPDATE sys_login_log set tenant_id = '2';
|
||||
UPDATE sys_role_dept set tenant_id = '2';
|
||||
UPDATE sys_role_menu set tenant_id = '2';
|
||||
UPDATE sys_user_post set tenant_id = '2';
|
||||
UPDATE sys_user_role set tenant_id = '2';
|
||||
UPDATE sys_dict_data set tenant_id = '2';
|
||||
UPDATE sys_dict_type set tenant_id = '2';
|
||||
|
@ -2,7 +2,7 @@ qiaoba:
|
||||
file-upload-path: C:/${spring.application.name}/uploadPath/
|
||||
dataSources:
|
||||
- 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://192.168.0.202:3306/${spring.application.name}?databaseTerm=SCHEMA&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true&allowMultiQueries=true
|
||||
username: root
|
||||
password: Root123456789.
|
||||
#连接池初始化大小
|
||||
@ -12,7 +12,7 @@ qiaoba:
|
||||
#最大连接池数量
|
||||
max-active: 20
|
||||
- driver: com.mysql.cj.jdbc.Driver
|
||||
url: jdbc:mysql://192.168.0.203:3306/${spring.application.name}?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true&allowMultiQueries=true
|
||||
url: jdbc:mysql://192.168.0.203:3306/${spring.application.name}?databaseTerm=SCHEMA&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true&allowMultiQueries=true
|
||||
username: root
|
||||
password: Root123456789.
|
||||
#连接池初始化大小
|
||||
@ -22,7 +22,16 @@ qiaoba:
|
||||
#最大连接池数量
|
||||
max-active: 20
|
||||
|
||||
|
||||
# - driver: org.postgresql.Driver
|
||||
# url: jdbc:postgresql://192.168.0.202:5432/mydb?currentSchema=qiaoba-boot&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true&allowMultiQueries=true
|
||||
# username: postgres
|
||||
# password: postgres
|
||||
# #连接池初始化大小
|
||||
# initial-size: 5
|
||||
# #最小空闲线程数
|
||||
# min-idle: 10
|
||||
# #最大连接池数量
|
||||
# max-active: 20
|
||||
mybatis-plus:
|
||||
configuration:
|
||||
# 自动驼峰命名规则(camel case)映射
|
||||
|
@ -5,6 +5,7 @@ spring:
|
||||
#新创建的bean覆盖旧的bean
|
||||
allow-bean-definition-overriding: true
|
||||
application:
|
||||
# DbName / FileDirName / SchemaPrefix
|
||||
name: qiaoba-boot
|
||||
profiles:
|
||||
active: dev
|
||||
|
@ -30,7 +30,12 @@ public enum DatasourceErrorCode {
|
||||
/**
|
||||
* 创建表错误
|
||||
*/
|
||||
CREATE_TABLE_ERROR(50104, "创建表错误");
|
||||
CREATE_TABLE_ERROR(50104, "创建表错误"),
|
||||
|
||||
/**
|
||||
* 创建模式/库错误
|
||||
*/
|
||||
CREATE_SCHEMA_ERROR(50105, "创建模式/库错误");
|
||||
|
||||
private final Integer code;
|
||||
private final String msg;
|
||||
|
@ -22,7 +22,7 @@ public enum DataBaseEnum {
|
||||
* MySQL
|
||||
*/
|
||||
MY_SQL("MySQL",
|
||||
"jdbc:mysql://{}:{}/{}?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true&allowMultiQueries=true",
|
||||
"jdbc:mysql://{}:{}/{}?databaseTerm=SCHEMA&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true&allowMultiQueries=true",
|
||||
"com.mysql.cj.jdbc.Driver",
|
||||
"SELECT 1"),
|
||||
|
||||
|
@ -8,6 +8,10 @@ import com.alibaba.druid.pool.DruidPooledConnection;
|
||||
import com.qiaoba.common.base.constants.TenantConstant;
|
||||
import com.qiaoba.common.base.context.BaseContext;
|
||||
import com.qiaoba.common.base.enums.BaseEnum;
|
||||
import com.qiaoba.common.database.context.BackupDatasourceContext;
|
||||
import com.qiaoba.common.database.context.DynamicDataSourceContext;
|
||||
import com.qiaoba.common.database.context.PrimaryDatasourceContext;
|
||||
import com.qiaoba.common.database.context.TenantDbTypeContext;
|
||||
import com.qiaoba.common.database.entity.DynamicDataSource;
|
||||
import com.qiaoba.common.database.monitor.NotOnlineDatasourceMonitor;
|
||||
import com.qiaoba.common.database.properties.DataSourceProperties;
|
||||
@ -24,7 +28,6 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* 多数据源配置
|
||||
@ -52,25 +55,11 @@ public class DynamicDataSourceConfig {
|
||||
*/
|
||||
public static Boolean COMPLETE_LOAD_DATASOURCE = false;
|
||||
|
||||
/**
|
||||
* 主要数据源
|
||||
*/
|
||||
public static Map<Object, Object> PRIMARY_DATASOURCE_MAP = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* 备用数据源-每次取0索引
|
||||
*/
|
||||
public static Map<String, List<DynamicDataSource>> BACKUP_DATASOURCE_MAP = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* 租户 ids
|
||||
*/
|
||||
public static List<String> TENANT_IDS = ListUtil.toList(TenantConstant.DEFAULT_TENANT_ID);
|
||||
|
||||
/**
|
||||
* 租户数据源类型
|
||||
*/
|
||||
public static Map<String, String> TENANT_DATASOURCE_TYPE_MAP = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* 把DynamicDataSourceContext 纳入容器管理,其他地方使用DynamicDataSourceConfig 类可以直接从容器取对象,并调用freshDataSource方法
|
||||
@ -85,7 +74,7 @@ public class DynamicDataSourceConfig {
|
||||
}
|
||||
|
||||
public static DruidDataSource getPrimaryDataSource(String tenantId) {
|
||||
return (DruidDataSource) PRIMARY_DATASOURCE_MAP.get(tenantId);
|
||||
return (DruidDataSource) PrimaryDatasourceContext.get(tenantId);
|
||||
}
|
||||
|
||||
private void initDefault() {
|
||||
@ -105,13 +94,13 @@ public class DynamicDataSourceConfig {
|
||||
}
|
||||
}
|
||||
|
||||
if (CollUtil.isEmpty(PRIMARY_DATASOURCE_MAP)) {
|
||||
if (CollUtil.isEmpty(PrimaryDatasourceContext.getAll())) {
|
||||
log.error("主系统配置数据源全部无效, 请检查 yml 中相关配置");
|
||||
}
|
||||
// 其他数据源备用
|
||||
addBackupMap(TenantConstant.DEFAULT_TENANT_ID, dataSources);
|
||||
// 刷新数据源
|
||||
this.dataSource.freshDataSource(PRIMARY_DATASOURCE_MAP);
|
||||
this.dataSource.freshDataSource(PrimaryDatasourceContext.getAll());
|
||||
}
|
||||
|
||||
private void initTenant() {
|
||||
@ -143,29 +132,25 @@ public class DynamicDataSourceConfig {
|
||||
}
|
||||
BaseContext.clearAllHolder();
|
||||
// 刷新数据源
|
||||
dataSource.freshDataSource(PRIMARY_DATASOURCE_MAP);
|
||||
dataSource.freshDataSource(PrimaryDatasourceContext.getAll());
|
||||
|
||||
}
|
||||
|
||||
public void deleteTenantDataSource(String tenantId) {
|
||||
PRIMARY_DATASOURCE_MAP.remove(tenantId);
|
||||
dataSource.freshDataSource(PRIMARY_DATASOURCE_MAP);
|
||||
}
|
||||
|
||||
public void changePrimaryDatasource(String tenantId, Object datasource) {
|
||||
PRIMARY_DATASOURCE_MAP.put(tenantId, datasource);
|
||||
PrimaryDatasourceContext.set(tenantId, datasource);
|
||||
// 将数据源的类型保存
|
||||
DruidPooledConnection connection = null;
|
||||
try {
|
||||
connection = ((DruidDataSource) datasource).getConnection();
|
||||
TENANT_DATASOURCE_TYPE_MAP.put(tenantId, connection.getMetaData().getDatabaseProductName());
|
||||
TenantDbTypeContext.set(tenantId, connection.getMetaData().getDatabaseProductName());
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
// 归还 connection
|
||||
IoUtil.close(connection);
|
||||
}
|
||||
dataSource.freshDataSource(PRIMARY_DATASOURCE_MAP);
|
||||
dataSource.freshDataSource(PrimaryDatasourceContext.getAll());
|
||||
}
|
||||
|
||||
public static Object buildDataSource(String tenantId, DynamicDataSource dynamicDataSource) {
|
||||
@ -183,7 +168,7 @@ public class DynamicDataSourceConfig {
|
||||
dataSource.setInitialSize(dynamicDataSource.getInitialSize());
|
||||
dataSource.setMinIdle(dynamicDataSource.getMinIdle());
|
||||
dataSource.setMaxActive(dynamicDataSource.getMaxActive());
|
||||
//dataSource.setKeepAlive(false);
|
||||
|
||||
try {
|
||||
// 初始化数据源
|
||||
dataSource.init();
|
||||
@ -199,7 +184,7 @@ public class DynamicDataSourceConfig {
|
||||
*/
|
||||
@PreDestroy
|
||||
public void close() {
|
||||
Set<Map.Entry<Object, Object>> entries = PRIMARY_DATASOURCE_MAP.entrySet();
|
||||
Set<Map.Entry<Object, Object>> entries = PrimaryDatasourceContext.getAll().entrySet();
|
||||
for (Map.Entry<Object, Object> entry : entries) {
|
||||
DruidDataSource dataSource = (DruidDataSource) entry.getValue();
|
||||
IoUtil.close(dataSource);
|
||||
@ -209,10 +194,10 @@ public class DynamicDataSourceConfig {
|
||||
private void addPrimaryMap(String tenantId, Object dataSource) {
|
||||
if (Objects.nonNull(dataSource)) {
|
||||
try {
|
||||
PRIMARY_DATASOURCE_MAP.put(tenantId, dataSource);
|
||||
PrimaryDatasourceContext.set(tenantId, dataSource);
|
||||
// 将数据源的类型保存
|
||||
DruidPooledConnection connection = ((DruidDataSource) dataSource).getConnection();
|
||||
TENANT_DATASOURCE_TYPE_MAP.put(tenantId, connection.getMetaData().getDatabaseProductName());
|
||||
TenantDbTypeContext.set(tenantId, connection.getMetaData().getDatabaseProductName());
|
||||
// 归还 connection
|
||||
IoUtil.close(connection);
|
||||
} catch (SQLException e) {
|
||||
@ -223,7 +208,7 @@ public class DynamicDataSourceConfig {
|
||||
|
||||
private void addBackupMap(String tenantId, List<DynamicDataSource> dataSources) {
|
||||
if (CollUtil.isNotEmpty(dataSources)) {
|
||||
BACKUP_DATASOURCE_MAP.put(tenantId, dataSources);
|
||||
BackupDatasourceContext.set(tenantId, dataSources);
|
||||
}
|
||||
}
|
||||
|
||||
@ -235,6 +220,6 @@ public class DynamicDataSourceConfig {
|
||||
public void setDefaultSetting() {
|
||||
BaseContext.setDataSource(TenantConstant.DEFAULT_TENANT_ID);
|
||||
BaseContext.setTenantId(TenantConstant.DEFAULT_TENANT_ID);
|
||||
BaseContext.setDatabaseType(TENANT_DATASOURCE_TYPE_MAP.get(TenantConstant.DEFAULT_TENANT_ID));
|
||||
BaseContext.setDatabaseType(TenantDbTypeContext.getDefault());
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,44 @@
|
||||
package com.qiaoba.common.database.context;
|
||||
|
||||
import com.qiaoba.common.database.entity.DynamicDataSource;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* 备用(未在使用)数据源
|
||||
*
|
||||
* @author ailanyin
|
||||
* @version 1.0
|
||||
* @since 2023/6/28 10:36
|
||||
*/
|
||||
public class BackupDatasourceContext {
|
||||
|
||||
/**
|
||||
* 备用数据源
|
||||
*/
|
||||
private static Map<String, List<DynamicDataSource>> BACKUP_DATASOURCE_MAP = new ConcurrentHashMap<>();
|
||||
|
||||
|
||||
/**
|
||||
* 获取租户备用数据源
|
||||
*
|
||||
* @param tenantId 租户ID
|
||||
* @return 数据源集合
|
||||
*/
|
||||
public static List<DynamicDataSource> get(String tenantId) {
|
||||
return BACKUP_DATASOURCE_MAP.get(tenantId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置租户备用数据源
|
||||
*
|
||||
* @param tenantId 租户ID
|
||||
* @param list 数据源集合
|
||||
*/
|
||||
public static void set(String tenantId, List<DynamicDataSource> list) {
|
||||
BACKUP_DATASOURCE_MAP.put(tenantId, list);
|
||||
}
|
||||
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package com.qiaoba.common.database.config;
|
||||
package com.qiaoba.common.database.context;
|
||||
|
||||
import com.qiaoba.common.base.constants.TenantConstant;
|
||||
import com.qiaoba.common.base.context.BaseContext;
|
@ -0,0 +1,63 @@
|
||||
package com.qiaoba.common.database.context;
|
||||
|
||||
import com.qiaoba.common.base.constants.TenantConstant;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* 主要(正在使用)数据源
|
||||
*
|
||||
* @author ailanyin
|
||||
* @version 1.0
|
||||
* @since 2023/6/28 10:36
|
||||
*/
|
||||
public class PrimaryDatasourceContext {
|
||||
|
||||
/**
|
||||
* 主要数据源
|
||||
*/
|
||||
private static Map<Object, Object> PRIMARY_DATASOURCE_MAP = new ConcurrentHashMap<>();
|
||||
|
||||
public static Map<Object, Object> getAll() {
|
||||
return PRIMARY_DATASOURCE_MAP;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取租户主要数据源
|
||||
*
|
||||
* @param tenantId 租户ID
|
||||
* @return 数据源
|
||||
*/
|
||||
public static Object get(String tenantId) {
|
||||
return PRIMARY_DATASOURCE_MAP.get(tenantId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取默认(主)主要数据源
|
||||
*
|
||||
* @return 数据源
|
||||
*/
|
||||
public static Object getDefault() {
|
||||
return PRIMARY_DATASOURCE_MAP.get(TenantConstant.DEFAULT_TENANT_ID);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置租户主要数据源
|
||||
*
|
||||
* @param tenantId 租户ID
|
||||
* @param datasource 数据源
|
||||
*/
|
||||
public static void set(String tenantId, Object datasource) {
|
||||
PRIMARY_DATASOURCE_MAP.put(tenantId, datasource);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除
|
||||
*
|
||||
* @param tenantId 租户ID
|
||||
*/
|
||||
public static void remove(String tenantId) {
|
||||
PRIMARY_DATASOURCE_MAP.remove(tenantId);
|
||||
}
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
package com.qiaoba.common.database.context;
|
||||
|
||||
import com.qiaoba.common.base.constants.TenantConstant;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* 租户数据源类型
|
||||
*
|
||||
* @author ailanyin
|
||||
* @version 1.0
|
||||
* @since 2023/6/28 10:25
|
||||
*/
|
||||
public class TenantDbTypeContext {
|
||||
|
||||
/**
|
||||
* 租户数据源类型
|
||||
*/
|
||||
private static Map<String, String> TENANT_DATASOURCE_TYPE_MAP = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* 获取租户数据源类型
|
||||
*
|
||||
* @param tenantId 租户ID
|
||||
* @return 数据源类型
|
||||
*/
|
||||
public static String get(String tenantId) {
|
||||
return TENANT_DATASOURCE_TYPE_MAP.get(tenantId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置租户数据源类型
|
||||
*
|
||||
* @param tenantId 租户ID
|
||||
* @param dbType 数据源类型
|
||||
*/
|
||||
public static void set(String tenantId, String dbType) {
|
||||
TENANT_DATASOURCE_TYPE_MAP.put(tenantId, dbType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取默认(主)数据源类型
|
||||
*
|
||||
* @return 数据源类型
|
||||
*/
|
||||
public static String getDefault() {
|
||||
return TENANT_DATASOURCE_TYPE_MAP.get(TenantConstant.DEFAULT_TENANT_ID);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置默认(主)数据源类型
|
||||
*
|
||||
* @param dbType 数据源类型
|
||||
*/
|
||||
public static void set(String dbType) {
|
||||
set(TenantConstant.DEFAULT_TENANT_ID, dbType);
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
package com.qiaoba.common.database.factories;
|
||||
|
||||
import com.qiaoba.common.database.config.DynamicDataSourceConfig;
|
||||
import com.qiaoba.common.database.config.DynamicDataSourceContext;
|
||||
import com.qiaoba.common.database.context.DynamicDataSourceContext;
|
||||
import com.qiaoba.common.database.properties.DataSourceProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
@ -1,17 +1,19 @@
|
||||
package com.qiaoba.common.database.interceptors;
|
||||
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.db.sql.SqlExecutor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
|
||||
import com.qiaoba.common.base.constants.BaseConstant;
|
||||
import com.qiaoba.common.base.constants.TenantConstant;
|
||||
import com.qiaoba.common.base.context.BaseContext;
|
||||
import com.qiaoba.common.database.utils.JdbcUtil;
|
||||
import com.qiaoba.common.database.utils.SqlUtil;
|
||||
import com.qiaoba.common.database.context.TenantDbTypeContext;
|
||||
import com.qiaoba.common.database.utils.DbUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.ibatis.executor.statement.StatementHandler;
|
||||
import org.postgresql.jdbc.PgConnection;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
@ -21,17 +23,22 @@ import java.util.Objects;
|
||||
* @version 1.0
|
||||
* @since 2023-04-25 22:48:43
|
||||
*/
|
||||
@Slf4j
|
||||
public class SchemaInterceptor implements InnerInterceptor {
|
||||
|
||||
@Value("${spring.application.name}")
|
||||
private String schemaPrefix;
|
||||
|
||||
@Override
|
||||
public void beforePrepare(StatementHandler sh, Connection conn, Integer transactionTimeout) {
|
||||
|
||||
if (Objects.nonNull(BaseContext.isSchemaMode()) && BaseContext.isSchemaMode()) {
|
||||
try {
|
||||
conn.unwrap(PgConnection.class).setSchema("qiaoba-boot-2");
|
||||
// schemaPrefix + '-' + tenantId
|
||||
// eg: schema = qiaoba-boot-2
|
||||
DbUtil.setSchema(TenantDbTypeContext.getDefault(), conn, schemaPrefix + BaseConstant.HYPHEN_JOIN_STR + BaseContext.getTenantId());
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
log.info("切换SCHEMA失败, 原因: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ 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.context.BackupDatasourceContext;
|
||||
import com.qiaoba.common.database.entity.DynamicDataSource;
|
||||
import com.qiaoba.common.database.utils.JdbcUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@ -98,9 +99,9 @@ public class NotOnlineDatasourceMonitor {
|
||||
}
|
||||
|
||||
private void addBackupMap(String tenantId, DynamicDataSource dataSource) {
|
||||
List<DynamicDataSource> dataSourceList = DynamicDataSourceConfig.BACKUP_DATASOURCE_MAP.get(tenantId);
|
||||
List<DynamicDataSource> dataSourceList = BackupDatasourceContext.get(tenantId);
|
||||
if (CollUtil.isEmpty(dataSourceList)) {
|
||||
DynamicDataSourceConfig.BACKUP_DATASOURCE_MAP.put(tenantId, ListUtil.toList(dataSource));
|
||||
BackupDatasourceContext.set(tenantId, ListUtil.toList(dataSource));
|
||||
} else {
|
||||
dataSourceList.add(dataSource);
|
||||
}
|
||||
|
@ -8,6 +8,8 @@ import com.baomidou.lock.LockTemplate;
|
||||
import com.qiaoba.common.base.constants.TenantConstant;
|
||||
import com.qiaoba.common.base.enums.DataBaseEnum;
|
||||
import com.qiaoba.common.database.config.DynamicDataSourceConfig;
|
||||
import com.qiaoba.common.database.context.BackupDatasourceContext;
|
||||
import com.qiaoba.common.database.context.PrimaryDatasourceContext;
|
||||
import com.qiaoba.common.database.entity.DynamicDataSource;
|
||||
import com.qiaoba.common.database.service.DynamicDatasourceService;
|
||||
import com.qiaoba.common.database.utils.JdbcUtil;
|
||||
@ -78,8 +80,7 @@ public class OnlineDatasourceMonitor {
|
||||
|
||||
private void tryConnect() {
|
||||
for (String tenantId : DynamicDataSourceConfig.TENANT_IDS) {
|
||||
Object primary = DynamicDataSourceConfig.PRIMARY_DATASOURCE_MAP.get(tenantId);
|
||||
DynamicDataSourceConfig.BACKUP_DATASOURCE_MAP.get(tenantId);
|
||||
Object primary = PrimaryDatasourceContext.get(tenantId);
|
||||
if (Objects.isNull(primary)) {
|
||||
// 说明初始化主要数据源的时候出错
|
||||
log.error("租户[{}]-目前主数据源异常, 开始切换备用数据源", tenantId);
|
||||
@ -110,7 +111,7 @@ public class OnlineDatasourceMonitor {
|
||||
// 关闭原有异常数据源
|
||||
IoUtil.close(dataSource);
|
||||
// 在数据源Map中删除
|
||||
DynamicDataSourceConfig.PRIMARY_DATASOURCE_MAP.remove(tenantId);
|
||||
PrimaryDatasourceContext.remove(tenantId);
|
||||
}
|
||||
// 将原有异常数据源加入到错误数据源Map, 等待重试
|
||||
addErrorDatasource(tenantId, dataSource);
|
||||
@ -130,7 +131,7 @@ public class OnlineDatasourceMonitor {
|
||||
*/
|
||||
private Boolean backToPrimary(String tenantId) {
|
||||
// 备用数据源
|
||||
List<DynamicDataSource> dataSources = DynamicDataSourceConfig.BACKUP_DATASOURCE_MAP.get(tenantId);
|
||||
List<DynamicDataSource> dataSources = BackupDatasourceContext.get(tenantId);
|
||||
if (CollUtil.isEmpty(dataSources)) {
|
||||
log.error("租户:[{}]切换备用数据源失败, 原因: 没有备用数据源", tenantId);
|
||||
return false;
|
||||
|
@ -1,23 +1,36 @@
|
||||
package com.qiaoba.module.tenant.utils;
|
||||
package com.qiaoba.common.database.utils;
|
||||
|
||||
import com.qiaoba.common.base.enums.DataBaseEnum;
|
||||
import org.apache.ibatis.jdbc.ScriptRunner;
|
||||
import org.postgresql.jdbc.PgConnection;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* 数据库工具类
|
||||
*
|
||||
* @author ailanyin
|
||||
* @version 1.0
|
||||
* @since 2023/6/27 16:11
|
||||
* @since 2023/6/28 8:52
|
||||
*/
|
||||
public class DbUtil {
|
||||
|
||||
public static void setSchema(String dbType, Connection conn, String schema) throws SQLException {
|
||||
|
||||
if (DataBaseEnum.MY_SQL.getType().equals(dbType)) {
|
||||
conn.setSchema(schema);
|
||||
} else if (DataBaseEnum.ORACLE.getType().equals(dbType)) {
|
||||
|
||||
} else if (DataBaseEnum.POSTGRE_SQL.getType().equals(dbType)) {
|
||||
conn.unwrap(PgConnection.class).setSchema(schema);
|
||||
} else if (DataBaseEnum.SQL_SERVER.getType().equals(dbType)) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public static void runScript(Connection conn, String filePath) throws IOException {
|
||||
ClassPathResource resource = new ClassPathResource(filePath);
|
||||
File file = resource.getFile();
|
@ -8,10 +8,7 @@ import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.Statement;
|
||||
import java.sql.*;
|
||||
|
||||
/**
|
||||
* JdbcUtil
|
||||
@ -104,6 +101,18 @@ public class JdbcUtil {
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println(check("oracle.jdbc.OracleDriver", "jdbc:oracle:thin:@//192.168.0.205:1521/ORCL", "system", "root"));
|
||||
Connection connection = getConnection("oracle.jdbc.OracleDriver", "jdbc:oracle:thin:@//192.168.0.205:1521/ORCL", "system", "root");
|
||||
try {
|
||||
DbUtil.setSchema("Oracle",connection,"scott");
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
try {
|
||||
connection.close();
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
System.out.println();
|
||||
}
|
||||
}
|
||||
|
@ -1,28 +0,0 @@
|
||||
package com.qiaoba.common.database.utils;
|
||||
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
|
||||
/**
|
||||
* SqlUtil
|
||||
*
|
||||
* @author ailanyin
|
||||
* @version 1.0
|
||||
* @since 2023/6/27 17:30
|
||||
*/
|
||||
public class SqlUtil {
|
||||
|
||||
public static void runSql(Connection connection, String sql) throws SQLException {
|
||||
|
||||
Statement statement = null;
|
||||
try {
|
||||
statement = connection.createStatement();
|
||||
statement.execute(sql);
|
||||
} finally {
|
||||
IoUtil.close(statement);
|
||||
}
|
||||
}
|
||||
}
|
@ -11,6 +11,7 @@ import com.qiaoba.api.system.entity.dto.DataScopeDto;
|
||||
import com.qiaoba.api.system.entity.dto.SysRoleDto;
|
||||
import com.qiaoba.api.system.entity.param.SysRoleParam;
|
||||
import com.qiaoba.auth.utils.SecurityUtil;
|
||||
import com.qiaoba.common.base.context.BaseContext;
|
||||
import com.qiaoba.common.base.exceptions.ServiceException;
|
||||
import com.qiaoba.common.database.entity.PageQuery;
|
||||
import com.qiaoba.common.database.entity.TableDataInfo;
|
||||
|
@ -7,6 +7,7 @@ import com.qiaoba.common.base.constants.BaseConstant;
|
||||
import com.qiaoba.common.base.constants.TenantConstant;
|
||||
import com.qiaoba.common.base.context.BaseContext;
|
||||
import com.qiaoba.common.database.config.DynamicDataSourceConfig;
|
||||
import com.qiaoba.common.database.context.TenantDbTypeContext;
|
||||
import com.qiaoba.common.web.utils.ResponseUtil;
|
||||
import com.qiaoba.common.web.utils.UriUtil;
|
||||
import com.qiaoba.module.tenant.entity.SysTenant;
|
||||
@ -91,12 +92,12 @@ public class DynamicDataSourceFilter extends OncePerRequestFilter {
|
||||
if (sysTenant.getMode().equals(SysTenant.DATASOURCE_MODE)) {
|
||||
//设置当前租户对应的数据源
|
||||
BaseContext.setDataSource(sysTenant.getTenantId());
|
||||
BaseContext.setDatabaseType(DynamicDataSourceConfig.TENANT_DATASOURCE_TYPE_MAP.get(sysTenant.getTenantId()));
|
||||
BaseContext.setDatabaseType(TenantDbTypeContext.get(sysTenant.getTenantId()));
|
||||
}
|
||||
// 字段模式 or Schema模式-数据源选择默认数据源
|
||||
else {
|
||||
BaseContext.setDataSource(TenantConstant.DEFAULT_TENANT_ID);
|
||||
BaseContext.setDatabaseType(DynamicDataSourceConfig.TENANT_DATASOURCE_TYPE_MAP.get(TenantConstant.DEFAULT_TENANT_ID));
|
||||
BaseContext.setDatabaseType(TenantDbTypeContext.getDefault());
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,13 +1,14 @@
|
||||
package com.qiaoba.module.tenant.init.impl;
|
||||
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.db.sql.SqlExecutor;
|
||||
import cn.hutool.http.HttpStatus;
|
||||
import com.qiaoba.common.base.code.DatasourceErrorCode;
|
||||
import com.qiaoba.common.database.utils.SqlUtil;
|
||||
import com.qiaoba.common.database.context.TenantDbTypeContext;
|
||||
import com.qiaoba.common.database.utils.DbUtil;
|
||||
import com.qiaoba.module.tenant.entity.vo.TenantInitVo;
|
||||
import com.qiaoba.module.tenant.init.InitTablesStrategy;
|
||||
import com.qiaoba.module.tenant.utils.DbUtil;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.IOException;
|
||||
@ -23,21 +24,32 @@ import java.sql.SQLException;
|
||||
*/
|
||||
@Service
|
||||
public class MysqlInitTablesStrategy implements InitTablesStrategy {
|
||||
|
||||
public static final String CREATE_MYSQL_DB_SQL = "DROP DATABASE IF EXISTS `{}`;CREATE DATABASE `{}` CHARACTER SET 'utf8mb4' COLLATE 'utf8mb4_general_ci';";
|
||||
|
||||
@Override
|
||||
public TenantInitVo create(Connection conn, String schema) {
|
||||
|
||||
// 切换 Schema
|
||||
try {
|
||||
SqlUtil.runSql(conn, StrUtil.format("use `{}`;", schema));
|
||||
DbUtil.runScript(conn, "MySQL/table/base_tables");
|
||||
conn.commit();
|
||||
return new TenantInitVo(HttpStatus.HTTP_OK, null);
|
||||
// 创建库
|
||||
SqlExecutor.execute(conn, StrUtil.format(CREATE_MYSQL_DB_SQL, schema, schema));
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
IoUtil.close(conn);
|
||||
return new TenantInitVo(DatasourceErrorCode.CREATE_SCHEMA_ERROR.getCode(), e.getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
// 切换 Schema
|
||||
DbUtil.setSchema(TenantDbTypeContext.getDefault(), conn, schema);
|
||||
DbUtil.runScript(conn, "MySQL/table/base_tables");
|
||||
return new TenantInitVo(HttpStatus.HTTP_OK, null);
|
||||
} catch (SQLException e) {
|
||||
return new TenantInitVo(DatasourceErrorCode.SWITCH_SCHEMA_ERROR.getCode(), e.getMessage());
|
||||
} catch (IOException e) {
|
||||
return new TenantInitVo(DatasourceErrorCode.CREATE_TABLE_ERROR.getCode(), e.getMessage());
|
||||
} finally {
|
||||
IoUtil.close(conn);
|
||||
}
|
||||
|
||||
return new TenantInitVo(DatasourceErrorCode.CREATE_TABLE_ERROR.getCode(), null);
|
||||
}
|
||||
}
|
||||
|
@ -4,9 +4,10 @@ import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.http.HttpStatus;
|
||||
import com.alibaba.druid.pool.DruidDataSource;
|
||||
import com.qiaoba.common.base.code.DatasourceErrorCode;
|
||||
import com.qiaoba.common.base.constants.TenantConstant;
|
||||
import com.qiaoba.common.base.constants.BaseConstant;
|
||||
import com.qiaoba.common.base.enums.DataBaseEnum;
|
||||
import com.qiaoba.common.database.config.DynamicDataSourceConfig;
|
||||
import com.qiaoba.common.database.context.PrimaryDatasourceContext;
|
||||
import com.qiaoba.common.database.context.TenantDbTypeContext;
|
||||
import com.qiaoba.common.database.utils.JdbcUtil;
|
||||
import com.qiaoba.module.tenant.entity.SysTenant;
|
||||
import com.qiaoba.module.tenant.entity.SysTenantDatasource;
|
||||
@ -17,6 +18,7 @@ import com.qiaoba.module.tenant.service.SysTenantDatasourceService;
|
||||
import com.qiaoba.module.tenant.service.SysTenantInitService;
|
||||
import com.qiaoba.module.tenant.service.SysTenantService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.sql.Connection;
|
||||
@ -36,6 +38,8 @@ public class SysTenantInitServiceImpl implements SysTenantInitService {
|
||||
private final SysTenantService sysTenantService;
|
||||
private final SysTenantDatasourceService sysTenantDatasourceService;
|
||||
|
||||
@Value("${spring.application.name}")
|
||||
private String schemaPrefix;
|
||||
|
||||
@Override
|
||||
public TenantInitCheckVo check(String tenantId) {
|
||||
@ -77,11 +81,11 @@ public class SysTenantInitServiceImpl implements SysTenantInitService {
|
||||
if (SysTenant.SCHEMA_MODE.equals(sysTenant.getMode())) {
|
||||
Connection connection = null;
|
||||
try {
|
||||
String schema = "qiaoba-boot-2";
|
||||
String schema = schemaPrefix + BaseConstant.HYPHEN_JOIN_STR + tenantId;
|
||||
// 获取主库Connection 和 Schema
|
||||
DruidDataSource dataSource = (DruidDataSource) DynamicDataSourceConfig.PRIMARY_DATASOURCE_MAP.get(TenantConstant.DEFAULT_TENANT_ID);
|
||||
DruidDataSource dataSource = (DruidDataSource) PrimaryDatasourceContext.getDefault();
|
||||
connection = dataSource.getConnection();
|
||||
return InitTablesStrategyFactory.getStrategy(DynamicDataSourceConfig.TENANT_DATASOURCE_TYPE_MAP.get(TenantConstant.DEFAULT_TENANT_ID)).create(connection, schema);
|
||||
return InitTablesStrategyFactory.getStrategy(TenantDbTypeContext.getDefault()).create(connection, schema);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
|
Reference in New Issue
Block a user