初始化项目
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.xujun</groupId>
|
||||
<artifactId>xtools-boot-db</artifactId>
|
||||
<version>5.1.0</version>
|
||||
</parent>
|
||||
<artifactId>xtools-boot-db-mybatis</artifactId>
|
||||
|
||||
<!-- 依赖 -->
|
||||
<dependencies>
|
||||
<!-- xtools-boot begin -->
|
||||
<!-- xtools-boot-core -->
|
||||
<dependency>
|
||||
<groupId>org.xujun</groupId>
|
||||
<artifactId>xtools-boot-core</artifactId>
|
||||
</dependency>
|
||||
<!-- xtools-boot-log -->
|
||||
<dependency>
|
||||
<groupId>org.xujun</groupId>
|
||||
<artifactId>xtools-boot-log</artifactId>
|
||||
</dependency>
|
||||
<!-- xtools-boot-thread -->
|
||||
<dependency>
|
||||
<groupId>org.xujun</groupId>
|
||||
<artifactId>xtools-boot-thread</artifactId>
|
||||
</dependency>
|
||||
<!-- xtools-boot end -->
|
||||
|
||||
<!-- SpringBoot begin -->
|
||||
<!-- MyBatis -->
|
||||
<dependency>
|
||||
<groupId>org.mybatis.spring.boot</groupId>
|
||||
<artifactId>mybatis-spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
<!-- SpringBoot end -->
|
||||
|
||||
<!-- MySQL 驱动 -->
|
||||
<dependency>
|
||||
<groupId>com.mysql</groupId>
|
||||
<artifactId>mysql-connector-j</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- fastjson2 -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.fastjson2</groupId>
|
||||
<artifactId>fastjson2</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package xtools.boot.db.mybatis;
|
||||
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import xtools.boot.core.utils.ModuleLoadUtils;
|
||||
import xtools.boot.db.mybatis.selector.BootDbMybatisImportSelector;
|
||||
|
||||
/**
|
||||
* <p>Title : BootDbMybatisConfiguration</p>
|
||||
* <p>Description : BootDbMybatisConfiguration</p>
|
||||
* <p>DevelopTools : Idea_x64_v2026.1</p>
|
||||
* <p>DevelopSystem : macOS Sequoia 15.7.5</p>
|
||||
* <p>Company : org.xujun</p>
|
||||
*
|
||||
* @author : XuJun
|
||||
* @version : 5.0.0
|
||||
* @date : 2026/01/01 09:30
|
||||
*/
|
||||
@MapperScan({"xtools.boot.db.mybatis.mapper"})
|
||||
@Import(BootDbMybatisImportSelector.class)
|
||||
public class BootDbMybatisConfiguration {
|
||||
|
||||
/**
|
||||
* 构造方法
|
||||
*/
|
||||
public BootDbMybatisConfiguration() {
|
||||
ModuleLoadUtils.loadSuccess(BootDbMybatisConfiguration.class);
|
||||
}
|
||||
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
package xtools.boot.db.mybatis.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* <p>Title : MyBatisConfig</p>
|
||||
* <p>Description : MyBatisConfig</p>
|
||||
* <p>DevelopTools : Idea_x64_v2026.1</p>
|
||||
* <p>DevelopSystem : macOS Sequoia 15.7.5</p>
|
||||
* <p>Company : org.xujun</p>
|
||||
*
|
||||
* @author : XuJun
|
||||
* @version : 5.0.0
|
||||
* @date : 2026/2/11 14:46
|
||||
*/
|
||||
@Data
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = "mybatis")
|
||||
public class MyBatisConfig {
|
||||
|
||||
/**
|
||||
* 是否开启MyBatis监控
|
||||
*/
|
||||
private Boolean monitor = true;
|
||||
|
||||
/**
|
||||
* 慢查询时间阈值
|
||||
*/
|
||||
private Integer slowTime = 3000;
|
||||
}
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
package xtools.boot.db.mybatis.enums;
|
||||
|
||||
import lombok.Getter;
|
||||
import xtools.boot.api.enums.BaseEnum;
|
||||
|
||||
/**
|
||||
* <p>Title : MySqlMonitorEnums</p>
|
||||
* <p>Description : MySqlMonitorEnums</p>
|
||||
* <p>DevelopTools : Idea_x64_v2026.1</p>
|
||||
* <p>DevelopSystem : macOS Sequoia 15.7.5</p>
|
||||
* <p>Company : org.xujun</p>
|
||||
*
|
||||
* @author : XuJun
|
||||
* @version : 5.0.0
|
||||
* @date : 2026/2/3 10:31
|
||||
*/
|
||||
public enum MySqlMonitorEnums implements BaseEnum {
|
||||
|
||||
// 进程列表
|
||||
PROCESSLIST(1, "进程列表", "SHOW FULL PROCESSLIST"),
|
||||
// 变量
|
||||
VARIABLES(2, "变量", "SHOW GLOBAL VARIABLES"),
|
||||
// 状态
|
||||
STATUS(3, "状态", "SHOW STATUS"),
|
||||
// 表信息
|
||||
TABLE(1000, "表信息", "SHOW TABLE STATUS"),
|
||||
|
||||
// 需要权限
|
||||
// // 查看正在进行中的事务
|
||||
// INNODB_TRX(100, "查看正在进行中的事务", "SELECT * FROM information_schema.INNODB_TRX"),
|
||||
// // 查看正在锁的事务
|
||||
// INNODB_LOCKS(101, "查看正在锁的事务", "SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS"),
|
||||
// // 查看等待锁的事务
|
||||
// INNODB_LOCK_WAITS(102, "查看等待锁的事务", "SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS"),
|
||||
// // 查询是否锁表
|
||||
// LOCK_TABLES(103, "查询是否锁表", "SHOW OPEN TABLES where In_use > 0"),
|
||||
// // 查看最近死锁的日志
|
||||
// INNODB_STATUS(104, "查看最近死锁的日志", "show engine innodb status"),
|
||||
|
||||
;
|
||||
|
||||
/**
|
||||
* 编码
|
||||
*/
|
||||
private final int code;
|
||||
|
||||
/**
|
||||
* 说明
|
||||
*/
|
||||
private final String desc;
|
||||
|
||||
/**
|
||||
* SQL语句
|
||||
*/
|
||||
@Getter
|
||||
private final String sql;
|
||||
|
||||
/**
|
||||
* 初始化方法
|
||||
*
|
||||
* @param code Code
|
||||
* @param desc 说明
|
||||
* @param sql SQL
|
||||
*/
|
||||
MySqlMonitorEnums(int code, String desc, String sql) {
|
||||
this.code = code;
|
||||
this.desc = desc;
|
||||
this.sql = sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断枚举值类型
|
||||
*
|
||||
* @param code 枚举值
|
||||
* @return 枚举值类型
|
||||
*/
|
||||
public static MySqlMonitorEnums valueOf(int code) {
|
||||
for (MySqlMonitorEnums type : values()) {
|
||||
if (type.code == code) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("unknown code, code=" + code);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有枚举
|
||||
*
|
||||
* @return 所有枚举
|
||||
*/
|
||||
@Override
|
||||
public BaseEnum[] all() {
|
||||
return values();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取枚举编码
|
||||
*
|
||||
* @return 枚举编码
|
||||
*/
|
||||
@Override
|
||||
public int code() {
|
||||
return code;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取枚举说明
|
||||
*
|
||||
* @return 枚举说明
|
||||
*/
|
||||
@Override
|
||||
public String desc() {
|
||||
return desc;
|
||||
}
|
||||
|
||||
}
|
||||
+214
@@ -0,0 +1,214 @@
|
||||
package xtools.boot.db.mybatis.interceptor;
|
||||
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.apache.ibatis.executor.statement.StatementHandler;
|
||||
import org.apache.ibatis.mapping.BoundSql;
|
||||
import org.apache.ibatis.mapping.ParameterMapping;
|
||||
import org.apache.ibatis.plugin.Interceptor;
|
||||
import org.apache.ibatis.plugin.Intercepts;
|
||||
import org.apache.ibatis.plugin.Invocation;
|
||||
import org.apache.ibatis.plugin.Signature;
|
||||
import org.apache.ibatis.reflection.MetaObject;
|
||||
import org.apache.ibatis.reflection.SystemMetaObject;
|
||||
import org.apache.ibatis.session.ResultHandler;
|
||||
import org.jspecify.annotations.NonNull;
|
||||
import org.springframework.stereotype.Component;
|
||||
import xtools.boot.api.model.dto.log.LogTrack;
|
||||
import xtools.boot.db.mybatis.config.MyBatisConfig;
|
||||
import xtools.boot.log.LogBus;
|
||||
import xtools.boot.log.enums.LogBusBaseType;
|
||||
import xtools.boot.log.holder.LogTrackHolder;
|
||||
import xtools.boot.thread.utils.VirtualThreadTaskUtils;
|
||||
import xtools.core.CollectionUtils;
|
||||
import xtools.core.enums.LogLevel;
|
||||
import xtools.core.enums.TimePattern;
|
||||
import xtools.core.time.InstantUtils;
|
||||
|
||||
import java.sql.Statement;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import static xtools.base.config.Constants.CP_COLON;
|
||||
import static xtools.base.config.Constants.CP_COMMA;
|
||||
import static xtools.base.config.Constants.CP_NUM0;
|
||||
import static xtools.base.config.Constants.CP_NUM1;
|
||||
import static xtools.base.config.Constants.CP_SPACE;
|
||||
|
||||
/**
|
||||
* <p>Title : MyBatisInterceptor</p>
|
||||
* <p>Description : MyBatisInterceptor</p>
|
||||
* <p>DevelopTools : Idea_x64_v2026.1</p>
|
||||
* <p>DevelopSystem : macOS Sequoia 15.7.5</p>
|
||||
* <p>Company : org.xujun</p>
|
||||
*
|
||||
* @author : XuJun
|
||||
* @version : 5.0.0
|
||||
* @date : 2026/2/11 14:35
|
||||
*/
|
||||
@Component
|
||||
@Intercepts({
|
||||
@Signature(type = StatementHandler.class, method = MyBatisInterceptor.QUERY, args = {Statement.class, ResultHandler.class}),
|
||||
@Signature(type = StatementHandler.class, method = MyBatisInterceptor.UPDATE, args = {Statement.class}),
|
||||
@Signature(type = StatementHandler.class, method = MyBatisInterceptor.BATCH, args = {Statement.class})
|
||||
})
|
||||
public class MyBatisInterceptor implements Interceptor {
|
||||
|
||||
/**
|
||||
* 查询方法名
|
||||
*/
|
||||
public final static String QUERY = "query";
|
||||
|
||||
/**
|
||||
* 更新方法名
|
||||
*/
|
||||
public final static String UPDATE = "update";
|
||||
|
||||
/**
|
||||
* 批量方法名
|
||||
*/
|
||||
public final static String BATCH = "batch";
|
||||
|
||||
@Resource
|
||||
private MyBatisConfig conf;
|
||||
|
||||
/**
|
||||
* 拦截操作
|
||||
*
|
||||
* @param invocation 调用参数
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public Object intercept(@NonNull Invocation invocation) throws Throwable {
|
||||
LogTrack log = LogTrackHolder.getDefNull();
|
||||
// 是否启动监控
|
||||
if (!conf.getMonitor() || Objects.isNull(log) || !log.save()) {
|
||||
return invocation.proceed();
|
||||
}
|
||||
// 执行结果
|
||||
Object result = null;
|
||||
// 异常信息
|
||||
Exception err = null;
|
||||
// 开始时间
|
||||
long startTime = System.currentTimeMillis();
|
||||
try {
|
||||
result = invocation.proceed();
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
err = e;
|
||||
throw e;
|
||||
} finally {
|
||||
// 结束时间
|
||||
long endTime = System.currentTimeMillis();
|
||||
// 获取当前线程日志信息
|
||||
LogTrack logTrack = LogTrackHolder.get();
|
||||
// 执行结果信息
|
||||
final Object execResult = result;
|
||||
// 执行异常
|
||||
final Exception exception = err;
|
||||
// 保存日志
|
||||
VirtualThreadTaskUtils.simple(() -> saveLog(logTrack, invocation, execResult, exception, startTime, endTime));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存日志信息
|
||||
*
|
||||
* @param logTrack 日志信息
|
||||
* @param invocation 调用参数
|
||||
* @param execResult 执行结果
|
||||
* @param exception 执行异常
|
||||
* @param startTime 开始时间
|
||||
* @param endTime 结束时间
|
||||
*/
|
||||
private void saveLog(LogTrack logTrack, Invocation invocation, Object execResult, Exception exception, long startTime, long endTime) {
|
||||
// 获取运行参数
|
||||
Object runArg = invocation.getArgs()[CP_NUM0];
|
||||
// 读写标识
|
||||
String rw = QUERY.equals(invocation.getMethod().getName()) ? "读" : "写";
|
||||
// 执行时间
|
||||
long execTime = endTime - startTime;
|
||||
// 是否为慢SQL
|
||||
boolean slowSql = execTime >= conf.getSlowTime();
|
||||
// SQL语句
|
||||
String sql;
|
||||
// SQL参数
|
||||
List<Object> params = null;
|
||||
if (invocation.getTarget() instanceof StatementHandler statementHandler) {
|
||||
BoundSql boundSql = statementHandler.getBoundSql();
|
||||
sql = boundSql.getSql();
|
||||
params = getBoundSqlParams(boundSql);
|
||||
} else {
|
||||
sql = runArg.toString();
|
||||
// 获取完整SQL
|
||||
sql = sql.substring(sql.indexOf(CP_COLON) + CP_NUM1);
|
||||
}
|
||||
// 格式化Sql
|
||||
sql = sql
|
||||
.replace(CP_COMMA, ", ")
|
||||
.replaceAll("\t+", CP_SPACE)
|
||||
.replaceAll("\n+", CP_SPACE)
|
||||
.replaceAll(" +", CP_SPACE)
|
||||
.trim();
|
||||
// 分装日志信息
|
||||
JSONObject logData = JSONObject.of(
|
||||
"startTime", InstantUtils.format(InstantUtils.from(startTime), TimePattern.ALL),
|
||||
"execTime", execTime,
|
||||
"result", execResult,
|
||||
"slowSql", slowSql,
|
||||
"sql", sql,
|
||||
"params", params
|
||||
);
|
||||
// 日志级别
|
||||
LogLevel level = LogLevel.INFO;
|
||||
if (Objects.nonNull(exception)) {
|
||||
level = LogLevel.ERROR;
|
||||
} else if (slowSql) {
|
||||
level = LogLevel.WARN;
|
||||
}
|
||||
LogBus.init(level, LogBusBaseType.MYBATIS, logTrack).title("SQL/" + rw).data(logData).error(exception).save();
|
||||
}
|
||||
|
||||
/**
|
||||
* 从BoundSql中获取参数值
|
||||
*
|
||||
* @param boundSql MyBatis的BoundSql
|
||||
* @return 参数集合
|
||||
*/
|
||||
private List<Object> getBoundSqlParams(BoundSql boundSql) {
|
||||
if (Objects.isNull(boundSql)) {
|
||||
return null;
|
||||
}
|
||||
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
|
||||
if (CollectionUtils.isEmpty(parameterMappings)) {
|
||||
return null;
|
||||
}
|
||||
Object parameterObject = boundSql.getParameterObject();
|
||||
Map<String, Object> additionalParams = boundSql.getAdditionalParameters();
|
||||
List<Object> params = new ArrayList<>(parameterMappings.size());
|
||||
MetaObject metaObject = Objects.isNull(parameterObject) ? null : SystemMetaObject.forObject(parameterObject);
|
||||
for (ParameterMapping mapping : parameterMappings) {
|
||||
String property = mapping.getProperty();
|
||||
Object value;
|
||||
// 优先从additionalParameters获取(MyBatis-Plus分页参数等动态参数在这里)
|
||||
if (additionalParams.containsKey(property)) {
|
||||
value = additionalParams.get(property);
|
||||
} else if (Objects.nonNull(metaObject) && metaObject.hasGetter(property)) {
|
||||
value = metaObject.getValue(property);
|
||||
} else if (parameterObject instanceof Map<?, ?> map) {
|
||||
try {
|
||||
value = map.get(property);
|
||||
} catch (Exception e) {
|
||||
value = "[无法解析:" + property + "]";
|
||||
}
|
||||
} else {
|
||||
value = parameterObject;
|
||||
}
|
||||
params.add(value);
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
package xtools.boot.db.mybatis.mapper;
|
||||
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* <p>Title : MonitorDatabasesMapper</p>
|
||||
* <p>Description : MonitorDatabasesMapper</p>
|
||||
* <p>DevelopTools : Idea_x64_v2026.1</p>
|
||||
* <p>DevelopSystem : macOS Sequoia 15.7.5</p>
|
||||
* <p>Company : org.xujun</p>
|
||||
*
|
||||
* @author : XuJun
|
||||
* @version : 5.0.0
|
||||
* @date : 2026/2/14 11:49
|
||||
*/
|
||||
@Mapper
|
||||
public interface MonitorDatabasesMapper {
|
||||
|
||||
/**
|
||||
* 监控MySQL
|
||||
*
|
||||
* @param sql SQL语句
|
||||
* @return 监控数据
|
||||
*/
|
||||
@Select({
|
||||
"<script>",
|
||||
"${sql}",
|
||||
"</script>"
|
||||
})
|
||||
List<JSONObject> mysql(@Param("sql") String sql);
|
||||
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
package xtools.boot.db.mybatis.monitor;
|
||||
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Component;
|
||||
import xtools.boot.api.exection.BizError;
|
||||
import xtools.boot.db.mybatis.enums.MySqlMonitorEnums;
|
||||
import xtools.boot.db.mybatis.mapper.MonitorDatabasesMapper;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* <p>Title : MySqlMonitor</p>
|
||||
* <p>Description : MySqlMonitor</p>
|
||||
* <p>DevelopTools : Idea_x64_v2026.1</p>
|
||||
* <p>DevelopSystem : macOS Sequoia 15.7.5</p>
|
||||
* <p>Company : org.xujun</p>
|
||||
*
|
||||
* @author : XuJun
|
||||
* @version : 5.0.0
|
||||
* @date : 2026/2/14 16:18
|
||||
*/
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class MySqlMonitor {
|
||||
|
||||
private final MonitorDatabasesMapper monitorDatabasesMapper;
|
||||
|
||||
/**
|
||||
* 获取MySQL监控信息
|
||||
*
|
||||
* @param type 枚举
|
||||
* @return MySQL监控信息
|
||||
*/
|
||||
public List<JSONObject> info(MySqlMonitorEnums type) {
|
||||
if (Objects.isNull(type)) {
|
||||
throw new BizError("类型参数不能为空");
|
||||
}
|
||||
return monitorDatabasesMapper.mysql(type.getSql());
|
||||
}
|
||||
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
package xtools.boot.db.mybatis.selector;
|
||||
|
||||
import org.jspecify.annotations.NonNull;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
|
||||
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
|
||||
/**
|
||||
* <p>Title : BootDbMybatisImportSelector</p>
|
||||
* <p>Description : BootDbMybatisImportSelector</p>
|
||||
* <p>DevelopTools : Idea_x64_v2026.1</p>
|
||||
* <p>DevelopSystem : macOS Sequoia 15.7.5</p>
|
||||
* <p>Company : org.xujun</p>
|
||||
*
|
||||
* @author : XuJun
|
||||
* @version : 5.0.0
|
||||
* @date : 2026/01/01 09:30
|
||||
*/
|
||||
public class BootDbMybatisImportSelector implements ImportBeanDefinitionRegistrar {
|
||||
|
||||
/**
|
||||
* 根据给定的注释元数据,根据需要注册bean
|
||||
*
|
||||
* @param importingClassMetadata AnnotationMetadata
|
||||
* @param registry BeanDefinitionRegistry
|
||||
*/
|
||||
@Override
|
||||
public void registerBeanDefinitions(@NonNull AnnotationMetadata importingClassMetadata, @NonNull BeanDefinitionRegistry registry) {
|
||||
// 构建扫描对象
|
||||
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry, true);
|
||||
// 扫描包下路径
|
||||
scanner.scan("xtools.boot.db.mybatis");
|
||||
}
|
||||
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
xtools.boot.db.mybatis.BootDbMybatisConfiguration
|
||||
Reference in New Issue
Block a user