add
This commit is contained in:
@ -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>
|
||||
|
||||
|
@ -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) {
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Reference in New Issue
Block a user