Mybatis-Plus系列---【自定义拦截器实现sql完整拼接及耗时打印】
1.自定义sql拦截器(注意:Executor是org.apache.ibatis.executor.Executor包下的,不要导错了)
import cn.hutool.core.util.ObjectUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.type.TypeHandlerRegistry;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.regex.Matcher;
@Intercepts({
@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}),
@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})
})
@Slf4j
public class SqlPrintInterceptor implements Interceptor {
// --- 1. 定义颜色常量 ---
private static final String ANSI_RESET = "\u001B[0m";
private static final String ANSI_RED = "\u001B[31m"; // 红色
private static final String ANSI_YELLOW = "\u001B[33m"; // 黄色 (作为橘黄色替代)
// -----------------------
@Override
public Object intercept(Invocation invocation) throws Throwable {
long startTime = System.currentTimeMillis();
Object result;
try {
result = invocation.proceed();
} catch (Throwable t) {
// 如果执行报错,也记录一下结束时间
throw t;
}
long endTime = System.currentTimeMillis();
long costTime = endTime - startTime;
// 拼接完整sql
String fullSql = null;
try {
fullSql = buildCompleteSql(invocation);
} catch (Exception e) {
log.error("拼接完整sql失败", e);
fullSql = "SQL拼接失败";
}
// --- 2. 颜色与输出逻辑 ---
// 构造日志内容
String logContent = String.format("\n【SQL执行信息】\n 执行耗时: %d ms\n 完整SQL: %s", costTime, fullSql);
// 判断时间:大于2000ms使用黄色,否则使用红色
// 注意:这里使用 System.out.println 是为了保证 ANSI 颜色代码不被某些日志框架过滤或转义
if (costTime > 2000) {
System.out.println(ANSI_YELLOW + "====== SLOW SQL WARNING ======" + logContent + ANSI_RESET);
} else {
System.out.println(ANSI_RED + "====== MyBatis SQL ======" + logContent + ANSI_RESET);
}
return result;
}
private String buildCompleteSql(Invocation invocation) {
// 获取MapperStatement和参数对象
MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
Object parameterObj = invocation.getArgs().length > 1 ? invocation.getArgs()[1] : null;
// 获取SQL模板和参数映射
Configuration configuration = mappedStatement.getConfiguration();
BoundSql boundSql = mappedStatement.getBoundSql(parameterObj);
String sqlTemplate = boundSql.getSql();
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
Object parameterObject = boundSql.getParameterObject();
// 去除sql中的多余空格
sqlTemplate = sqlTemplate.replaceAll("\\s+", " ");
// 替换占位符为实际参数
if (ObjectUtil.isNotEmpty(parameterMappings) && ObjectUtil.isNotEmpty(parameterObj)) {
TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
// 单个参数且有对应的TypeHandler
if (typeHandlerRegistry.hasTypeHandler(parameterObj.getClass())) {
sqlTemplate = sqlTemplate.replaceFirst("\\?", Matcher.quoteReplacement(getParamValue(parameterObj)));
} else {
// 多个参数,逐个替换
MetaObject metaObject = configuration.newMetaObject(parameterObject);
for (ParameterMapping mapping : parameterMappings) {
String paramName = mapping.getProperty();
Object paramValue = null;
if (metaObject.hasGetter(paramName)) {
paramValue = metaObject.getValue(paramName);
} else if (boundSql.hasAdditionalParameter(paramName)) {
paramValue = boundSql.getAdditionalParameter(paramName);
}
// 替换占位符,无参数则标记为"缺失"
String value = paramValue != null ? getParamValue(paramValue) : "缺失";
sqlTemplate = sqlTemplate.replaceFirst("\\?", Matcher.quoteReplacement(value));
}
}
}
return sqlTemplate;
}
private String getParamValue(Object paramValue) {
if (paramValue == null) {
return "null";
}
// 字符串类型加单引号
if (paramValue instanceof String) {
return "'" + paramValue.toString() + "'";
}
// --- 增强:日期类型也需要加单引号 ---
if (paramValue instanceof Date) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return "'" + sdf.format((Date) paramValue) + "'";
}
// 其他类型直接转字符串
return paramValue.toString();
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
}
2.注册拦截器
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* SQL 日志打印配置类
* 只有当配置文件中 mybatis-plus.sql-log.enabled = true 时,这个类才会生效
*/
@Configuration
// 核心控制开关:matchIfMissing = false 表示如果不配置这个属性,默认是不开启的
@ConditionalOnProperty(prefix = "mybatis-plus.sql-log", name = "enabled", havingValue = "true", matchIfMissing = false)
public class MybatisSqlLogConfig {
@Bean
public SqlPrintInterceptor sqlPrintInterceptor() {
return new SqlPrintInterceptor();
}
}
3.配置文件配置开启(可以不配置,不配的话默认关闭)
mybatis-plus.sql-log.enabled = true
愿你走出半生,归来仍是少年!
浙公网安备 33010602011771号