This commit is contained in:
2023-06-25 17:22:05 +08:00
parent 3fd3a09888
commit caeae9127f
51 changed files with 235 additions and 632 deletions

View File

@ -11,6 +11,8 @@
<artifactId>qiaoba-common-datasource</artifactId>
<description>通用-数据源模块</description>
<dependencies>
<!-- Druid -->
<dependency>
@ -38,7 +40,7 @@
</dependency>
<dependency>
<groupId>com.qiaoba</groupId>
<artifactId>qiaoba-common-web</artifactId>
<artifactId>qiaoba-common-redis</artifactId>
</dependency>
</dependencies>

View File

@ -1,16 +1,11 @@
package com.qiaoba.common.database.interceptors;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import com.qiaoba.common.base.context.BaseContext;
import com.qiaoba.common.database.handlers.schema.SchemaHandlerFactory;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.springframework.beans.factory.annotation.Value;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Objects;
/**
@ -23,7 +18,6 @@ import java.util.Objects;
public class SchemaInterceptor implements InnerInterceptor {
@Override
public void beforePrepare(StatementHandler sh, Connection conn, Integer transactionTimeout) {

View File

@ -1,308 +0,0 @@
package com.qiaoba.common.database.mapper;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.ReflectionKit;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.toolkit.Db;
import com.qiaoba.common.web.utils.BeanCopyUtil;
import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/**
* 自定义 Mapper 接口, 实现 自定义扩展
*
* @param <M> mapper 泛型
* @param <T> table 泛型
* @param <V> vo 泛型
* @author ailanyin
* @since 2023-04-23 17:20:32
*/
@SuppressWarnings("unchecked")
public interface BaseMapperPlus<M, T, V> extends BaseMapper<T> {
/**
* currentVoClass
*
* @return Vo
*/
default Class<V> currentVoClass() {
return (Class<V>) ReflectionKit.getSuperClassGenericType(this.getClass(), BaseMapperPlus.class, 2);
}
/**
* currentModelClass
*
* @return model
*/
default Class<T> currentModelClass() {
return (Class<T>) ReflectionKit.getSuperClassGenericType(this.getClass(), BaseMapperPlus.class, 1);
}
/**
* currentMapperClass
*
* @return mapper
*/
default Class<M> currentMapperClass() {
return (Class<M>) ReflectionKit.getSuperClassGenericType(this.getClass(), BaseMapperPlus.class, 0);
}
/**
* 查询所有
*
* @return all lst
*/
default List<T> selectList() {
return this.selectList(new QueryWrapper<>());
}
/**
* 批量新增
*
* @param entityList list
* @return 结果
*/
default boolean insertBatch(Collection<T> entityList) {
return Db.saveBatch(entityList);
}
/**
* 批量更新
*
* @param entityList list
* @return 结果
*/
default boolean updateBatchById(Collection<T> entityList) {
return Db.updateBatchById(entityList);
}
/**
* 批量插入或更新
*
* @param entityList list
* @return 结果
*/
default boolean insertOrUpdateBatch(Collection<T> entityList) {
return Db.saveOrUpdateBatch(entityList);
}
/**
* 批量插入(包含限制条数)
*
* @param entityList list
* @param batchSize 最大调数
* @return 结果
*/
default boolean insertBatch(Collection<T> entityList, int batchSize) {
return Db.saveBatch(entityList, batchSize);
}
/**
* 批量更新(包含限制条数)
*
* @param entityList list
* @param batchSize 最大调数
* @return 结果
*/
default boolean updateBatchById(Collection<T> entityList, int batchSize) {
return Db.updateBatchById(entityList, batchSize);
}
/**
* 批量插入或更新(包含限制条数)
*
* @param entityList list
* @param batchSize 最大调数
* @return 结果
*/
default boolean insertOrUpdateBatch(Collection<T> entityList, int batchSize) {
return Db.saveOrUpdateBatch(entityList, batchSize);
}
/**
* 插入或更新
*
* @param entity entity
* @return 结果
*/
default boolean insertOrUpdate(T entity) {
return Db.saveOrUpdate(entity);
}
/**
* 查询Vo by id
*
* @param id 主键
* @return Vo
*/
default V selectVoById(Serializable id) {
return selectVoById(id, this.currentVoClass());
}
/**
* 根据 ID 查询
*
* @param id 主键
* @param voClass Vo
* @param <C> 泛型
* @return Vo
*/
default <C> C selectVoById(Serializable id, Class<C> voClass) {
T obj = this.selectById(id);
if (ObjectUtil.isNull(obj)) {
return null;
}
return BeanCopyUtil.copy(obj, voClass);
}
/**
* 通过Ids 查询 list
*
* @param idList ids
* @return list
*/
default List<V> selectVoBatchIds(Collection<? extends Serializable> idList) {
return selectVoBatchIds(idList, this.currentVoClass());
}
/**
* 通过Ids 查询 list
*
* @param idList ids
* @param voClass Vo
* @param <C> 泛型
* @return list
*/
default <C> List<C> selectVoBatchIds(Collection<? extends Serializable> idList, Class<C> voClass) {
List<T> list = this.selectBatchIds(idList);
if (CollUtil.isEmpty(list)) {
return CollUtil.newArrayList();
}
return BeanCopyUtil.copyList(list, voClass);
}
/**
* selectVoByMap
*
* @param map map
* @return Vo
*/
default List<V> selectVoByMap(Map<String, Object> map) {
return selectVoByMap(map, this.currentVoClass());
}
/**
* selectVoByMap
*
* @param map map
* @param voClass Vo
* @param <C> 泛型
* @return Vo
*/
default <C> List<C> selectVoByMap(Map<String, Object> map, Class<C> voClass) {
List<T> list = this.selectByMap(map);
if (CollUtil.isEmpty(list)) {
return CollUtil.newArrayList();
}
return BeanCopyUtil.copyList(list, voClass);
}
/**
* 根据条件查询一条记录
*
* @param wrapper 条件
* @return Vo
*/
default V selectVoOne(Wrapper<T> wrapper) {
return selectVoOne(wrapper, this.currentVoClass());
}
/**
* 根据条件查询一条记录
*
* @param wrapper 条件
* @param voClass Vo
* @param <C> 泛型
* @return Vo
*/
default <C> C selectVoOne(Wrapper<T> wrapper, Class<C> voClass) {
T obj = this.selectOne(wrapper);
if (ObjectUtil.isNull(obj)) {
return null;
}
return BeanCopyUtil.copy(obj, voClass);
}
/**
* 根据条件查询所有
*
* @param wrapper 条件
* @return list
*/
default List<V> selectVoList(Wrapper<T> wrapper) {
return selectVoList(wrapper, this.currentVoClass());
}
/**
* 根据条件查询所有
*
* @param wrapper 条件
* @param voClass Vo
* @param <C> 泛型
* @return list vo
*/
default <C> List<C> selectVoList(Wrapper<T> wrapper, Class<C> voClass) {
List<T> list = this.selectList(wrapper);
if (CollUtil.isEmpty(list)) {
return CollUtil.newArrayList();
}
return BeanCopyUtil.copyList(list, voClass);
}
/**
* 分页查询Vo
*
* @param page 分页对象
* @param wrapper 条件
* @param <P> 泛型
* @return IPage
*/
default <P extends IPage<V>> P selectVoPage(IPage<T> page, Wrapper<T> wrapper) {
return selectVoPage(page, wrapper, this.currentVoClass());
}
/**
* 分页查询Vo
*
* @param page 分页对象
* @param wrapper 条件
* @param voClass Vo
* @param <C> 泛型
* @param <P> 泛型
* @return IPage
*/
default <C, P extends IPage<C>> P selectVoPage(IPage<T> page, Wrapper<T> wrapper, Class<C> voClass) {
IPage<T> pageData = this.selectPage(page, wrapper);
IPage<C> voPage = new Page<>(pageData.getCurrent(), pageData.getSize(), pageData.getTotal());
if (CollUtil.isEmpty(pageData.getRecords())) {
return (P) voPage;
}
voPage.setRecords(BeanCopyUtil.copyList(pageData.getRecords(), voClass));
return (P) voPage;
}
}

View File

@ -2,6 +2,8 @@ package com.qiaoba.common.database.monitor;
import cn.hutool.core.io.IoUtil;
import com.alibaba.druid.pool.DruidDataSource;
import com.baomidou.lock.LockInfo;
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;
@ -34,12 +36,16 @@ public class DatasourceConnectionMonitor {
private DynamicDataSourceConfig dynamicDataSourceConfig;
@Resource
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
public void init() {
// 1s运行一次
// 1s 运行一次
new Timer().schedule(new TimerTask() {
@Override
public void run() {
@ -47,47 +53,77 @@ public class DatasourceConnectionMonitor {
if (!DynamicDataSourceConfig.COMPLETE_LOAD_DATASOURCE) {
return;
}
log.trace("开始运行数据源监控线程, 时间: {}", new Date());
for (String tenantId : DynamicDataSourceConfig.TENANT_IDS) {
Object primary = DynamicDataSourceConfig.PRIMARY_DATASOURCE_MAP.get(tenantId);
if (Objects.isNull(primary)) {
// 说明初始化主要数据源的时候出错
log.error("租户[{}]-目前主数据源异常, 开始切换备用数据源", tenantId);
// 切换备用数据源
changePrimary(tenantId);
} else {
DruidDataSource dataSource = (DruidDataSource) primary;
try {
Connection connection = dataSource.getConnection();
if (check(connection, tenantId)) {
// 说明数据源正常
log.trace("租户[{}]-目前主数据源正常, 无需切换数据源", tenantId);
// 主数据 处理任务
if (TenantConstant.DEFAULT_TENANT_ID.equals(tenantId)) {
handleJob();
}
IoUtil.close(connection);
continue;
}
log.error("租户[{}]-目前主数据源异常, 开始切换备用数据源", tenantId);
IoUtil.close(connection);
// 主数据源异常 切换备用数据源
if (changePrimary(tenantId)) {
// 备用切换成功, 关闭原有异常数据源
IoUtil.close(dataSource);
}
} catch (SQLException e) {
//e.printStackTrace();
}
}
// expire = -1 锁自动续期, 防止数据源过多或异常等待, 超过默认锁 30s
final LockInfo lockInfo = lockTemplate.lock(LOCK_KEY, -1, 1000);
//申请锁失败 说明集群中其他设备正在执行监控
if (null == lockInfo) {
return;
}
//申请锁成功
try {
// 执行监控
datasourceConnectionMonitor();
} finally {
// 释放锁
lockTemplate.releaseLock(lockInfo);
}
log.trace("结束运行数据源监控线程, 时间: {}", new Date());
}
}, 0, 1000);
}
/**
* 核心监控内容
*/
private void datasourceConnectionMonitor() {
log.trace("开始运行数据源监控线程, 时间: {}", new Date());
for (String tenantId : DynamicDataSourceConfig.TENANT_IDS) {
Object primary = DynamicDataSourceConfig.PRIMARY_DATASOURCE_MAP.get(tenantId);
if (Objects.isNull(primary)) {
// 说明初始化主要数据源的时候出错
log.error("租户[{}]-目前主数据源异常, 开始切换备用数据源", tenantId);
// 切换备用数据源
changePrimary(tenantId);
} else {
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);
// 主数据源异常 切换备用数据源
if (changePrimary(tenantId)) {
// 备用切换成功, 关闭原有异常数据源
IoUtil.close(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;

View File

@ -8,7 +8,13 @@ import org.springframework.stereotype.Component;
import java.util.List;
/**
* DataSourceProperties
*
* @author ailanyin
* @version 1.0
* @since 2022-09-22 04:20:28
*/
@Component
@ConfigurationProperties(prefix = "qiaoba")
@Data

View File

@ -5,7 +5,8 @@ import cn.hutool.core.util.StrUtil;
import com.qiaoba.common.base.exceptions.ServiceException;
import lombok.extern.slf4j.Slf4j;
import java.sql.*;
import java.sql.Connection;
import java.sql.DriverManager;
/**
* JdbcUtil