初始化项目

This commit is contained in:
2026-08-06 15:19:45 +08:00
commit 63c8d8b757
229 changed files with 12653 additions and 0 deletions
+22
View File
@@ -0,0 +1,22 @@
<?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</artifactId>
<version>5.1.0</version>
</parent>
<packaging>pom</packaging>
<artifactId>xtools-boot-db</artifactId>
<!-- 子模块 -->
<modules>
<module>xtools-boot-db-druid</module>
<module>xtools-boot-db-hikari</module>
<module>xtools-boot-db-mybatis</module>
<module>xtools-boot-db-mybatis-plus</module>
</modules>
</project>
@@ -0,0 +1,37 @@
<?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-druid</artifactId>
<!-- 依赖 -->
<dependencies>
<!-- xtools-boot begin -->
<!-- xtools-boot-core -->
<dependency>
<groupId>org.xujun</groupId>
<artifactId>xtools-boot-core</artifactId>
</dependency>
<!-- xtools-boot end -->
<!-- Druid(数据库连接池) -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-4-starter</artifactId>
</dependency>
<!-- servlet-api -->
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,28 @@
package xtools.boot.db.druid;
import org.springframework.context.annotation.Import;
import xtools.boot.core.utils.ModuleLoadUtils;
import xtools.boot.db.druid.selector.BootDbDruidImportSelector;
/**
* <p>Title : BootDbDruidConfiguration</p>
* <p>Description : BootDbDruidConfiguration</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
*/
@Import(BootDbDruidImportSelector.class)
public class BootDbDruidConfiguration {
/**
* 构造方法
*/
public BootDbDruidConfiguration() {
ModuleLoadUtils.loadSuccess(BootDbDruidConfiguration.class);
}
}
@@ -0,0 +1,97 @@
package xtools.boot.db.druid.condig;
import com.alibaba.druid.spring.boot4.autoconfigure.properties.DruidStatProperties;
import com.alibaba.druid.util.Utils;
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.FilterConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import xtools.core.StringUtils;
import java.io.IOException;
/**
* <p>Title : DruidConf</p>
* <p>Description : DruidConf</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 : 2025年8月13日 上午8:06:06
*/
@Component
@Configuration
public class DruidConf {
/**
* 广告过滤
*
* @param properties Druid配置信息
* @return 广告过滤
*/
@Bean
@ConditionalOnProperty(name = "spring.datasource.type", havingValue = "com.alibaba.druid.pool.DruidDataSource")
@ConditionalOnProperty(name = "spring.datasource.druid.web-stat-filter.enabled", havingValue = "true")
public FilterRegistrationBean<Filter> removeDruidFilterRegistrationBean(DruidStatProperties properties) {
DruidStatProperties.StatViewServlet config = properties.getStatViewServlet();
String urlPattern = config.getUrlPattern();
// 提取common.js的配置路径
String pattern = StringUtils.isEmpty(urlPattern) ? "/druid/**" : urlPattern;
String commonJsPattern = pattern.replaceAll("\\*", "js/common.js");
final String filePath = "support/http/resources/js/common.js";
// 创建filter进行过滤
Filter filter = new Filter() {
/**
* 初始化
*
* @param filterConfig FilterConfig
*/
@Override
public void init(FilterConfig filterConfig) {
}
/**
* 过滤器
*
* @param request ServletRequest
* @param response ServletResponse
* @param chain FilterChain
*/
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
chain.doFilter(request, response);
// 重置缓冲区,响应头不会被重置
response.resetBuffer();
// 获取common.js
String text = Utils.readFromResource(filePath);
// 正则替换banner, 除去底部的广告信息
text = text.replaceAll("<a.*?banner\"></a><br/>", "");
text = text.replaceAll("powered.*?shrek.wang</a>", "");
response.getWriter().write(text);
}
/**
* 销毁
*/
@Override
public void destroy() {
}
};
FilterRegistrationBean<Filter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(filter);
registrationBean.addUrlPatterns(commonJsPattern);
return registrationBean;
}
}
@@ -0,0 +1,33 @@
package xtools.boot.db.druid.condig;
import org.springframework.stereotype.Component;
import xtools.boot.core.interfaces.FilterWhitelist;
import java.util.Set;
/**
* <p>Title : DruidFilterWhitelist</p>
* <p>Description : DruidFilterWhitelist</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/3/8 12:28
*/
@Component
public class DruidFilterWhitelist implements FilterWhitelist {
/**
* 添加过滤器白名单
*
* @return 过滤器白名单
*/
@Override
public Set<String> add() {
return Set.of(
"/druid/**"
);
}
}
@@ -0,0 +1,36 @@
package xtools.boot.db.druid.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 : BootDbDruidImportSelector</p>
* <p>Description : BootDbDruidImportSelector</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 BootDbDruidImportSelector 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.druid");
}
}
@@ -0,0 +1,30 @@
<?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-hikari</artifactId>
<!-- 依赖 -->
<dependencies>
<!-- xtools-boot begin -->
<!-- xtools-boot-core -->
<dependency>
<groupId>org.xujun</groupId>
<artifactId>xtools-boot-core</artifactId>
</dependency>
<!-- xtools-boot end -->
<!-- hikari -->
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,25 @@
package xtools.boot.db.hikari;
import xtools.boot.core.utils.ModuleLoadUtils;
/**
* <p>Title : BootDbHikariConfiguration</p>
* <p>Description : BootDbHikariConfiguration</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 BootDbHikariConfiguration {
/**
* 构造方法
*/
public BootDbHikariConfiguration() {
ModuleLoadUtils.loadSuccess(BootDbHikariConfiguration.class);
}
}
@@ -0,0 +1,37 @@
<?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-plus</artifactId>
<!-- 依赖 -->
<dependencies>
<!-- xtools-boot begin -->
<!-- xtools-boot-core -->
<dependency>
<groupId>org.xujun</groupId>
<artifactId>xtools-boot-core</artifactId>
</dependency>
<!-- xtools-boot end -->
<!-- SpringBoot begin -->
<!-- mybatis-plus begin -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-jsqlparser</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot4-starter</artifactId>
</dependency>
<!-- mybatis-plus end -->
<!-- SpringBoot end -->
</dependencies>
</project>
@@ -0,0 +1,28 @@
package xtools.boot.db.mybatisplus;
import org.springframework.context.annotation.Import;
import xtools.boot.core.utils.ModuleLoadUtils;
import xtools.boot.db.mybatisplus.selector.BootDbMybatisPlusImportSelector;
/**
* <p>Title : BootCacheRedisConfiguration</p>
* <p>Description : BootCacheRedisConfiguration</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
*/
@Import(BootDbMybatisPlusImportSelector.class)
public class BootDbMybatisPlusConfiguration {
/**
* 构造方法
*/
public BootDbMybatisPlusConfiguration() {
ModuleLoadUtils.loadSuccess(BootDbMybatisPlusConfiguration.class);
}
}
@@ -0,0 +1,35 @@
package xtools.boot.db.mybatisplus.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* <p>Title : MybatisPlusConfig</p>
* <p>Description : MybatisPlusConfig</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/1/5 10:15
*/
@Configuration
public class MybatisPlusConfig {
/**
* 配置 MybatisPlus 拦截器
*
* @return MybatisPlus 拦截器
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 添加分页插件
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
@@ -0,0 +1,36 @@
package xtools.boot.db.mybatisplus.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 : BootDbMybatisPlusImportSelector</p>
* <p>Description : BootDbMybatisPlusImportSelector</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 BootDbMybatisPlusImportSelector 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.mybatisplus");
}
}
@@ -0,0 +1,63 @@
package xtools.boot.db.mybatisplus.utils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import xtools.core.ArrUtils;
import java.time.Instant;
import java.util.Objects;
import static xtools.base.config.Constants.CP_NUM0;
import static xtools.base.config.Constants.CP_NUM1;
import static xtools.base.config.Constants.CP_NUM10;
import static xtools.base.config.Constants.CP_NUM2;
/**
* <p>Title : QueryUtils</p>
* <p>Description : QueryUtils</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/1/17 19:16
*/
public class QueryUtils {
/**
* 添加时间范围查询
*
* @param query 查询条件
* @param timeRange 时间范围
* @param timeField 时间字段
* @param <T> 泛型
*/
public static <T> void addTimeRange(LambdaQueryWrapper<T> query, Instant[] timeRange, SFunction<T, ?> timeField) {
if (ArrUtils.isEmpty(timeRange) || timeRange.length != CP_NUM2) {
return;
}
query.ge(Objects.nonNull(timeRange[CP_NUM0]), timeField, timeRange[CP_NUM0]);
query.le(Objects.nonNull(timeRange[CP_NUM1]), timeField, timeRange[CP_NUM1]);
}
/**
* 获取分页条件
*
* @param currentPage 当前页
* @param pageSize 页面大小
* @param <T> 泛型
* @return 分页条件
*/
public static <T> Page<T> getPage(Integer currentPage, Integer pageSize) {
if (Objects.isNull(currentPage) || currentPage < CP_NUM1) {
currentPage = CP_NUM1;
}
if (Objects.isNull(pageSize) || pageSize < CP_NUM1) {
pageSize = CP_NUM10;
}
return new Page<>(currentPage, pageSize);
}
}
@@ -0,0 +1 @@
xtools.boot.db.mybatisplus.BootDbMybatisPlusConfiguration
@@ -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>
@@ -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);
}
}
@@ -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;
}
@@ -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;
}
}
@@ -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;
}
}
@@ -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);
}
@@ -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());
}
}
@@ -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");
}
}