This commit is contained in:
2023-06-09 16:02:04 +08:00
parent ccf453b198
commit 4dcb7c297b
21 changed files with 412 additions and 167 deletions

View File

@ -31,6 +31,10 @@ qiaoba:
max: 20
is-use: false # 不使用该库
# 连接测试query配置检测连接是否有效
connectionTestQuery: SELECT 1
# 多久检查一次连接的活性
keepaliveTime: 30000
mybatis-plus:
configuration:

View File

@ -1,19 +0,0 @@
package com.qiaoba.common.base;
/**
* 数据源接口
*
* @author ailanyin
* @version 1.0
* @since 2023/6/8 17:23
*/
public interface DatasourceService {
/**
* 检查租户是否允许访问
*
* @param tenantId tenantId
* @return true = 允许
*/
boolean checkTenantInfo(String tenantId);
}

View File

@ -29,6 +29,11 @@ public class BaseConstant {
*/
public static final String COLON_JOIN_STR = ":";
/**
* 中划线拼接符号: '-'
*/
public static final String HYPHEN_JOIN_STR = "-";
/**
* 树的key的命名
*/

View File

@ -14,6 +14,11 @@ public class BaseContext {
*/
private static final ThreadLocal<String> DATABASE_TYPE_HOLDER = new ThreadLocal<>();
/**
* SCHEMA
*/
private static final ThreadLocal<Boolean> SCHEMA_HOLDER = new ThreadLocal<>();
/**
* 租户ID
*/
@ -66,6 +71,24 @@ public class BaseContext {
DATASOURCE_HOLDER.set(dataSource);
}
/**
* 是否是 Schema模式
*
* @return Boolean
*/
public static Boolean getSchema() {
return SCHEMA_HOLDER.get();
}
/**
* 设置是否是 Schema模式
*
* @param isSchema 是/否
*/
public static void setSchema(boolean isSchema) {
SCHEMA_HOLDER.set(isSchema);
}
/**
* 清除所有的ThreadLocal
*/
@ -76,7 +99,8 @@ public class BaseContext {
TENANT_ID_HOLDER.remove();
// 清除上下文中数据库类型
DATABASE_TYPE_HOLDER.remove();
// 清除上下文中 SCHEMA
SCHEMA_HOLDER.remove();
}

View File

@ -1,12 +1,18 @@
package com.qiaoba.common.database.config;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import com.alibaba.druid.pool.DruidDataSource;
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.constants.DynamicDatasourceConstant;
import com.qiaoba.common.database.entity.DynamicDataSource;
import com.qiaoba.common.database.properties.DefaultDataSourceProperties;
import com.qiaoba.common.database.properties.MasterInfo;
import com.qiaoba.common.database.properties.PoolInfo;
import com.qiaoba.common.database.properties.SlaveInfo;
import com.qiaoba.common.database.service.DynamicDatasourceService;
import com.qiaoba.common.database.utils.JdbcUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
@ -15,10 +21,7 @@ import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/**
@ -32,40 +35,88 @@ import java.util.concurrent.ConcurrentHashMap;
@Configuration
public class DynamicDataSourceConfig {
/**
* Bean 必须叫 dataSource
*/
@Resource
private DynamicDataSourceContext dataSource;
@Resource
private DefaultDataSourceProperties defaultDataSourceProperties;
@Resource
private DynamicDatasourceService dynamicDatasourceService;
public static Map<Object, Object> DATA_SOURCE_MAP = new ConcurrentHashMap<>();
/**
* 使用中的数据源
*/
public static Map<Object, Object> MASTER_SOURCE_MAP = new ConcurrentHashMap<>();
/**
* 备用数据源
*/
public static Map<String, List<SlaveInfo>> SLAVE_SOURCE_MAP = new ConcurrentHashMap<>();
/**
* 把DynamicDataSourceContext 纳入容器管理其他地方使用DynamicDataSourceConfig 类可以直接从容器取对象并调用freshDataSource方法
*/
@PostConstruct
public void init() {
// 加载系统默认数据源
initDefault();
// 加载租户数据源
initTenant();
}
private void initDefault() {
MasterInfo master = defaultDataSourceProperties.getMaster();
List<SlaveInfo> slaves = defaultDataSourceProperties.getSlaves();
addDataSourceToMap(DynamicDatasourceConstant.DEFAULT_MASTER_DATASOURCE_KEY, buildDataSource(master.getDriver(), master.getUrl(), master.getUsername(), master.getPassword(), master.getPool()));
// 将主数据源使用
addMasterMap(DynamicDatasourceConstant.MASTER_PREFIX + TenantConstant.DEFAULT_TENANT_ID, buildDataSource(master.getDriver(), master.getUrl(), master.getUsername(), master.getPassword(), master.getPool()));
dataSource.freshDataSource(MASTER_SOURCE_MAP);
// 将从数据源备用
if (CollUtil.isNotEmpty(slaves)) {
for (int i = 0; i < slaves.size(); i++) {
SlaveInfo slave = slaves.get(i);
if (slave.getIsUse()) {
addDataSourceToMap(DynamicDatasourceConstant.DEFAULT_SLAVE_DATASOURCE_PREFIX + i, buildDataSource(slave.getDriver(), slave.getUrl(), slave.getUsername(), slave.getPassword(), slave.getPool()));
if (!slaves.get(i).getIsUse()) {
slaves.remove(i);
}
}
if (CollUtil.isNotEmpty(slaves)) {
SLAVE_SOURCE_MAP.put(DynamicDatasourceConstant.SLAVE_PREFIX + TenantConstant.DEFAULT_TENANT_ID, slaves);
}
}
}
private void initTenant() {
setDefaultSetting();
List<DynamicDataSource> dynamicDataSourceList = dynamicDatasourceService.loadAllTenantDatasource();
for (DynamicDataSource dynamicDataSource : dynamicDataSourceList) {
// 将主数据源加载
if (BaseEnum.YES.getCode().equals(dynamicDataSource.getIsMaster())) {
addMasterMap(DynamicDatasourceConstant.MASTER_PREFIX + dynamicDataSource.getTenantId(), buildDataSource(dynamicDataSource.getDriver(), dynamicDataSource.getUrl(), dynamicDataSource.getUsername(), dynamicDataSource.getPassword(), new PoolInfo(dynamicDataSource.getInitCount(), dynamicDataSource.getMinCount(), dynamicDataSource.getMaxCount())));
dataSource.freshDataSource(MASTER_SOURCE_MAP);
}
// 将从数据源备用
else {
if (Objects.isNull(SLAVE_SOURCE_MAP.get(DynamicDatasourceConstant.MASTER_PREFIX + dynamicDataSource.getTenantId()))) {
List<SlaveInfo> slaves = new ArrayList<>();
slaves.add(datasourceToSlave(dynamicDataSource));
SLAVE_SOURCE_MAP.put(DynamicDatasourceConstant.MASTER_PREFIX + dynamicDataSource.getTenantId(), slaves);
} else {
List<SlaveInfo> slaves = SLAVE_SOURCE_MAP.get(DynamicDatasourceConstant.MASTER_PREFIX + dynamicDataSource.getTenantId());
slaves.add(datasourceToSlave(dynamicDataSource));
}
}
}
dataSource.freshDataSource(DATA_SOURCE_MAP);
BaseContext.clearAllHolder();
}
public void addTenantDataSource(String tenantId, String driver, String url, String username, String password, PoolInfo poolInfo) {
addDataSourceToMap(DynamicDatasourceConstant.MASTER_PREFIX + tenantId, buildDataSource(driver, url, username, password, poolInfo));
dataSource.freshDataSource(DATA_SOURCE_MAP);
addMasterMap(DynamicDatasourceConstant.MASTER_PREFIX + tenantId, buildDataSource(driver, url, username, password, poolInfo));
dataSource.freshDataSource(MASTER_SOURCE_MAP);
}
public void deleteTenantDataSource(String tenantId) {
DATA_SOURCE_MAP.remove(DynamicDatasourceConstant.MASTER_PREFIX + tenantId);
dataSource.freshDataSource(DATA_SOURCE_MAP);
MASTER_SOURCE_MAP.remove(DynamicDatasourceConstant.MASTER_PREFIX + tenantId);
dataSource.freshDataSource(MASTER_SOURCE_MAP);
}
private static Object buildDataSource(String driver, String url, String username, String password, PoolInfo poolInfo) {
@ -96,16 +147,27 @@ public class DynamicDataSourceConfig {
*/
@PreDestroy
public void close() {
Set<Map.Entry<Object, Object>> entries = DATA_SOURCE_MAP.entrySet();
Set<Map.Entry<Object, Object>> entries = MASTER_SOURCE_MAP.entrySet();
for (Map.Entry<Object, Object> entry : entries) {
DruidDataSource dataSource = (DruidDataSource) entry.getValue();
dataSource.close();
}
}
private void addDataSourceToMap(String name, Object dataSource) {
private void addMasterMap(String name, Object dataSource) {
if (Objects.nonNull(dataSource)) {
DATA_SOURCE_MAP.put(name, dataSource);
MASTER_SOURCE_MAP.put(name, dataSource);
}
}
private SlaveInfo datasourceToSlave(DynamicDataSource dynamicDataSource) {
SlaveInfo slaveInfo = BeanUtil.copyProperties(dynamicDataSource, SlaveInfo.class);
slaveInfo.setPool(new PoolInfo(dynamicDataSource.getInitCount(), dynamicDataSource.getMinCount(), dynamicDataSource.getMaxCount()));
return slaveInfo;
}
public void setDefaultSetting() {
BaseContext.setDataSource(DynamicDatasourceConstant.MASTER_PREFIX + TenantConstant.DEFAULT_TENANT_ID);
BaseContext.setTenantId(TenantConstant.DEFAULT_TENANT_ID);
}
}

View File

@ -1,5 +1,6 @@
package com.qiaoba.common.database.config;
import com.qiaoba.common.base.constants.TenantConstant;
import com.qiaoba.common.base.context.BaseContext;
import com.qiaoba.common.database.constants.DynamicDatasourceConstant;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
@ -20,8 +21,8 @@ public class DynamicDataSourceContext extends AbstractRoutingDataSource {
* 设置默认数据源、全部数据源,及刷新
*/
public void freshDataSource(Map<Object, Object> targetDataSources) {
//默认数据源
super.setDefaultTargetDataSource(targetDataSources.get(DynamicDatasourceConstant.DEFAULT_MASTER_DATASOURCE_KEY));
//默认数据源 (determineCurrentLookupKey 如果找不到就使用默认数据源)
super.setDefaultTargetDataSource(targetDataSources.get(DynamicDatasourceConstant.MASTER_PREFIX + TenantConstant.DEFAULT_TENANT_ID));
//设置全部数据源
super.setTargetDataSources(targetDataSources);
//刷新(即把targetDataSources刷到resolvedDataSources中去resolvedDataSources才是我们真正存放数据源的map)

View File

@ -6,6 +6,7 @@ import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
import com.qiaoba.common.base.context.BaseContext;
import com.qiaoba.common.database.interceptors.SchemaInterceptor;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.StringValue;
import org.springframework.context.annotation.Bean;
@ -31,6 +32,7 @@ public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new SchemaInterceptor());
interceptor.addInnerInterceptor(new TenantLineInnerInterceptor(new TenantLineHandler() {
@Override
public Expression getTenantId() {

View File

@ -9,16 +9,6 @@ package com.qiaoba.common.database.constants;
*/
public class DynamicDatasourceConstant {
/**
* 系统默认的主数据源名称
*/
public static final String DEFAULT_MASTER_DATASOURCE_KEY = "master_default";
/**
* 系统默认的从数据源名称
*/
public static final String DEFAULT_SLAVE_DATASOURCE_PREFIX = "slave_default_";
/**
* 主数据源拼接前缀 master_tenantId
*/

View File

@ -23,6 +23,11 @@ public class DynamicDataSource {
*/
private String tenantId;
/**
* 主库
*/
private String isMaster;
/**
* 数据库-url
*/
@ -43,4 +48,19 @@ public class DynamicDataSource {
*/
private String driver;
/**
* 连接池-初始化大小
*/
private Integer initCount;
/**
* 连接池-最小空闲数
*/
private Integer minCount;
/**
* 连接池-最大连接数
*/
private Integer maxCount;
}

View File

@ -2,7 +2,6 @@ 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.filters.DynamicDataSourceFilter;
import com.qiaoba.common.database.properties.DefaultDataSourceProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@ -27,11 +26,6 @@ public class DynamicDataSourceFactory {
return new DynamicDataSourceConfig();
}
@Bean
public DynamicDataSourceFilter handleTenantFilter() {
return new DynamicDataSourceFilter();
}
@Bean
public DefaultDataSourceProperties defaultDataSourceProperties() {
return new DefaultDataSourceProperties();

View File

@ -1,80 +0,0 @@
package com.qiaoba.common.database.filters;
import cn.hutool.core.util.StrUtil;
import com.qiaoba.common.base.DatasourceService;
import com.qiaoba.common.base.constants.TenantConstant;
import com.qiaoba.common.base.context.BaseContext;
import com.qiaoba.common.database.constants.DynamicDatasourceConstant;
import com.qiaoba.common.web.utils.ResponseUtil;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.annotation.Resource;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 动态切换不同租户的数据源
*
* @author ailanyin
* @version 1.0
* @since 2023-04-25 22:48:43
*/
@Component
@Order(-10000)
public class DynamicDataSourceFilter extends OncePerRequestFilter {
@Resource
private DatasourceService datasourceService;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String tenantId = request.getHeader(TenantConstant.HEADER_KEY_TENANT);
// 主库或没有tenantId
if (StrUtil.isBlank(tenantId)
|| TenantConstant.DEFAULT_TENANT_ID.equals(tenantId)
|| request.getRequestURI().startsWith("/tenant")) {
setDefaultSetting();
filterChain.doFilter(request, response);
return;
}
if (!checkTenantInfo(tenantId)) {
// 检查租户的信息,是否存在,是否过期,是否禁用
ResponseUtil.response(response, "租户可能到期或被禁用");
return;
}
before(tenantId);
filterChain.doFilter(request, response);
after();
}
private void before(String tenantId) {
//设置当前租户对应的数据源
BaseContext.setDataSource(DynamicDatasourceConstant.MASTER_PREFIX + tenantId);
//设置当前租户对应的租户ID
BaseContext.setTenantId(tenantId);
//设置当前租户对应的数据库类型
BaseContext.setDatabaseType("MySQL");
}
private void after() {
BaseContext.clearAllHolder();
}
private boolean checkTenantInfo(String tenantId) {
return datasourceService.checkTenantInfo(tenantId);
}
private void setDefaultSetting() {
BaseContext.setDataSource(DynamicDatasourceConstant.DEFAULT_MASTER_DATASOURCE_KEY);
//设置当前租户对应的租户ID
BaseContext.setTenantId(TenantConstant.DEFAULT_TENANT_ID);
//设置当前租户对应的数据库类型
BaseContext.setDatabaseType("MySQL");
}
}

View File

@ -1,24 +1,39 @@
package com.qiaoba.common.database.interceptors;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import lombok.extern.slf4j.Slf4j;
import com.qiaoba.common.base.context.BaseContext;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.springframework.beans.factory.annotation.Value;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Objects;
@Slf4j
/**
* SchemaInterceptor
*
* @author ailanyin
* @version 1.0
* @since 2023-04-25 22:48:43
*/
public class SchemaInterceptor implements InnerInterceptor {
@Value("${spring.application.name}")
private String baseDatabase;
@Override
public void beforePrepare(StatementHandler sh, Connection conn, Integer transactionTimeout) {
String sql = "use `qiaoba-boot`;";
try {
conn.createStatement().execute(sql);
} catch (SQLException e) {
throw new RuntimeException(e);
}
if (Objects.nonNull(BaseContext.getSchema()) && BaseContext.getSchema()) {
// use qiaoba-1;
String sql = StrUtil.format("use `{}-{}`;", baseDatabase, BaseContext.getTenantId());
try {
conn.createStatement().execute(sql);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
InnerInterceptor.super.beforePrepare(sh, conn, transactionTimeout);
}

View File

@ -0,0 +1,22 @@
package com.qiaoba.common.database.service;
import com.qiaoba.common.database.entity.DynamicDataSource;
import java.util.List;
/**
* 动态数据源接口
*
* @author ailanyin
* @version 1.0
* @since 2023/6/9 13:15
*/
public interface DynamicDatasourceService {
/**
* 加载所有租户的数据源
*
* @return 数据源集合
*/
List<DynamicDataSource> loadAllTenantDatasource();
}

View File

@ -35,7 +35,7 @@ public class SysTenant extends BaseEntity {
/**
* 数据库模式
*/
public static final String DATABASE_MODE = "2";
public static final String SCHEMA_MODE = "2";
/**
* 数据源模式

View File

@ -0,0 +1,36 @@
package com.qiaoba.module.tenant.entity.param;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import java.io.Serializable;
/**
* 租户数据源查询参数
*
* @author ailanyin
* @version 1.0
* @since 2023/6/9 13:18
*/
@Getter
@Setter
@NoArgsConstructor
public class SysTenantDatasourceParam implements Serializable {
private String tenantId;
private String isMaster;
private String isUse;
public SysTenantDatasourceParam(String tenantId) {
this.tenantId = tenantId;
}
public SysTenantDatasourceParam(String tenantId, String isMaster) {
this.tenantId = tenantId;
this.isMaster = isMaster;
}
}

View File

@ -0,0 +1,109 @@
package com.qiaoba.module.tenant.filters;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
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.config.DynamicDataSourceConfig;
import com.qiaoba.common.database.constants.DynamicDatasourceConstant;
import com.qiaoba.common.web.utils.ResponseUtil;
import com.qiaoba.module.tenant.entity.SysTenant;
import com.qiaoba.module.tenant.service.SysTenantService;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.annotation.Resource;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;
import java.util.Objects;
/**
* 动态切换不同租户的数据源
*
* @author ailanyin
* @version 1.0
* @since 2023-04-25 22:48:43
*/
@Component
@Order(-10000)
public class DynamicDataSourceFilter extends OncePerRequestFilter {
@Resource
private SysTenantService sysTenantService;
@Resource
private DynamicDataSourceConfig dynamicDataSourceConfig;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String tenantId = request.getHeader(TenantConstant.HEADER_KEY_TENANT);
// 主库或没有tenantId
if (StrUtil.isBlank(tenantId)
|| TenantConstant.DEFAULT_TENANT_ID.equals(tenantId)
|| request.getRequestURI().startsWith("/tenant")) {
dynamicDataSourceConfig.setDefaultSetting();
filterChain.doFilter(request, response);
return;
}
SysTenant sysTenant = sysTenantService.selectById(tenantId);
if (checkTenantIsNotAllow(response, sysTenant)) {
return;
}
before(sysTenant);
filterChain.doFilter(request, response);
after();
}
private void before(SysTenant sysTenant) {
BaseContext.setTenantId(sysTenant.getTenantId());
BaseContext.setSchema(sysTenant.getMode().equals(SysTenant.SCHEMA_MODE));
// 数据源模式-设置第三方数据源
if (sysTenant.getMode().equals(SysTenant.DATASOURCE_MODE)) {
//设置当前租户对应的数据源
BaseContext.setDataSource(DynamicDatasourceConstant.MASTER_PREFIX + sysTenant.getTenantId());
}
// 字段模式/Schema模式-数据源选择默认数据源
else {
BaseContext.setDataSource(DynamicDatasourceConstant.MASTER_PREFIX + TenantConstant.DEFAULT_TENANT_ID);
}
}
private void after() {
BaseContext.clearAllHolder();
}
/**
* 检查租户是否不允许访问
*
* @param sysTenant sysTenant
* @return 是->不允许
*/
private boolean checkTenantIsNotAllow(HttpServletResponse response, SysTenant sysTenant) throws IOException {
if (Objects.isNull(sysTenant)) {
// 未找到租户信息
ResponseUtil.errorAuth(response, 401, "未找到租户信息");
return true;
}
if (sysTenant.getStatus().equals(BaseEnum.ABNORMAL.getCode())) {
// 封禁状态
ResponseUtil.errorAuth(response, 401, "租户已被封禁");
return true;
}
if (DateUtil.compare(sysTenant.getExpireTime(), new Date()) < 0) {
// 已过期
ResponseUtil.errorAuth(response, 401, "租户已过期");
return true;
}
return false;
}
}

View File

@ -48,7 +48,9 @@ public class MysqlDataHandler implements DataHandler {
// 手动提交
conn.setAutoCommit(false);
// 创建表
initTables(conn);
if (needCreateTables) {
initTables(conn);
}
// 处理 sys_config
handleSysConfig(conn, tenantId);
// 处理 sys_post

View File

@ -1,6 +1,7 @@
package com.qiaoba.module.tenant.service;
import com.qiaoba.module.tenant.entity.SysTenantDatasource;
import com.qiaoba.module.tenant.entity.param.SysTenantDatasourceParam;
import java.util.List;
@ -52,4 +53,12 @@ public interface SysTenantDatasourceService {
* @return list
*/
List<SysTenantDatasource> selectList(String tenantId);
/**
* 查询租户数据源
*
* @param param 条件
* @return list
*/
List<SysTenantDatasource> selectList(SysTenantDatasourceParam param);
}

View File

@ -0,0 +1,48 @@
package com.qiaoba.module.tenant.service.impl;
import cn.hutool.core.bean.BeanUtil;
import com.qiaoba.common.base.enums.BaseEnum;
import com.qiaoba.common.base.enums.DataBaseEnum;
import com.qiaoba.common.database.entity.DynamicDataSource;
import com.qiaoba.common.database.service.DynamicDatasourceService;
import com.qiaoba.module.tenant.entity.SysTenantDatasource;
import com.qiaoba.module.tenant.entity.param.SysTenantDatasourceParam;
import com.qiaoba.module.tenant.service.SysTenantDatasourceService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
/**
* 动态数据源接口
*
* @author ailanyin
* @version 1.0
* @since 2023/6/9 13:16
*/
@Service
@RequiredArgsConstructor
public class DynamicDatasourceServiceImpl implements DynamicDatasourceService {
private final SysTenantDatasourceService sysTenantDatasourceService;
@Override
public List<DynamicDataSource> loadAllTenantDatasource() {
SysTenantDatasourceParam param = new SysTenantDatasourceParam();
param.setIsUse(BaseEnum.YES.getCode());
List<SysTenantDatasource> datasourceList = sysTenantDatasourceService.selectList(param);
return transform(datasourceList);
}
private List<DynamicDataSource> transform(List<SysTenantDatasource> datasourceList) {
List<DynamicDataSource> dynamicDataSourceList = new ArrayList<>();
for (SysTenantDatasource datasource : datasourceList) {
DynamicDataSource dynamicDataSource = BeanUtil.copyProperties(datasource, DynamicDataSource.class);
dynamicDataSource.setDriver(DataBaseEnum.getDriver(datasource.getType()));
dynamicDataSource.setUrl(DataBaseEnum.getUrl(datasource.getType(), datasource.getIp(), datasource.getPort(), datasource.getName()));
dynamicDataSourceList.add(dynamicDataSource);
}
return dynamicDataSourceList;
}
}

View File

@ -1,8 +1,10 @@
package com.qiaoba.module.tenant.service.impl;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.qiaoba.common.base.enums.BaseEnum;
import com.qiaoba.module.tenant.entity.SysTenantDatasource;
import com.qiaoba.module.tenant.entity.param.SysTenantDatasourceParam;
import com.qiaoba.module.tenant.mapper.SysTenantDatasourceMapper;
import com.qiaoba.module.tenant.service.SysTenantDatasourceService;
import lombok.RequiredArgsConstructor;
@ -25,11 +27,7 @@ public class SysTenantDatasourceServiceImpl implements SysTenantDatasourceServic
@Override
public SysTenantDatasource selectMaster(String tenantId) {
QueryWrapper<SysTenantDatasource> wrapper = new QueryWrapper<>();
wrapper.lambda()
.eq(SysTenantDatasource::getTenantId, tenantId)
.eq(SysTenantDatasource::getIsMaster, BaseEnum.YES.getCode());
return sysTenantDatasourceMapper.selectOne(wrapper);
return sysTenantDatasourceMapper.selectOne(paramToWrapper(new SysTenantDatasourceParam(tenantId, BaseEnum.YES.getCode())));
}
@Override
@ -49,9 +47,21 @@ public class SysTenantDatasourceServiceImpl implements SysTenantDatasourceServic
@Override
public List<SysTenantDatasource> selectList(String tenantId) {
return sysTenantDatasourceMapper.selectList(paramToWrapper(new SysTenantDatasourceParam(tenantId)));
}
@Override
public List<SysTenantDatasource> selectList(SysTenantDatasourceParam param) {
return sysTenantDatasourceMapper.selectList(paramToWrapper(param));
}
private QueryWrapper<SysTenantDatasource> paramToWrapper(SysTenantDatasourceParam param) {
QueryWrapper<SysTenantDatasource> wrapper = new QueryWrapper<>();
wrapper.lambda()
.eq(SysTenantDatasource::getTenantId, tenantId);
return sysTenantDatasourceMapper.selectList(wrapper);
.eq(StrUtil.isNotBlank(param.getTenantId()), SysTenantDatasource::getTenantId, param.getTenantId())
.eq(StrUtil.isNotBlank(param.getIsUse()), SysTenantDatasource::getIsUse, param.getIsUse())
.eq(StrUtil.isNotBlank(param.getIsMaster()), SysTenantDatasource::getIsMaster, param.getIsMaster());
return wrapper;
}
}

View File

@ -3,7 +3,7 @@ package com.qiaoba.module.tenant.service.impl;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.qiaoba.auth.utils.SecurityUtil;
import com.qiaoba.common.base.DatasourceService;
import com.qiaoba.common.base.constants.BaseConstant;
import com.qiaoba.common.base.enums.BaseEnum;
import com.qiaoba.common.base.enums.DataBaseEnum;
import com.qiaoba.common.base.exceptions.ServiceException;
@ -38,7 +38,7 @@ import java.util.Objects;
*/
@Service
@RequiredArgsConstructor
public class SysTenantServiceImpl implements SysTenantService, DatasourceService {
public class SysTenantServiceImpl implements SysTenantService {
@Value("${qiaoba.datasource.master.driver}")
private String driver;
@ -143,8 +143,8 @@ public class SysTenantServiceImpl implements SysTenantService, DatasourceService
if (SysTenant.COLUMN_MODE.equals(sysTenant.getMode())) {
return column();
}
if (SysTenant.DATABASE_MODE.equals(sysTenant.getMode())) {
return database(sysTenant.getTenantId());
if (SysTenant.SCHEMA_MODE.equals(sysTenant.getMode())) {
return schema(sysTenant.getTenantId());
}
if (SysTenant.DATASOURCE_MODE.equals(sysTenant.getMode())) {
return datasource(sysTenant.getTenantId());
@ -163,8 +163,8 @@ public class SysTenantServiceImpl implements SysTenantService, DatasourceService
/**
* 处理数据库模式
*/
private Connection database(String tenantId) {
String realUrl = this.url.replaceFirst(baseDatabase, baseDatabase + "-" + tenantId);
private Connection schema(String tenantId) {
String realUrl = this.url.replaceFirst(baseDatabase, baseDatabase + BaseConstant.HYPHEN_JOIN_STR + tenantId);
return JdbcUtil.getConnection(driver, realUrl, username, password);
}
@ -179,13 +179,4 @@ public class SysTenantServiceImpl implements SysTenantService, DatasourceService
return JdbcUtil.getConnection(DataBaseEnum.getDriver(master.getType()), DataBaseEnum.getUrl(master.getType(), master.getIp(), master.getPort(), master.getName()), master.getUsername(), master.getPassword());
}
@Override
public boolean checkTenantInfo(String tenantId) {
SysTenant sysTenant = selectById(tenantId);
if (Objects.isNull(sysTenant)) {
return false;
}
// todo 检查租户有没有过期/禁用
return sysTenant.getStatus().equals(BaseEnum.NORMAL.getCode());
}
}