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

posted on 2025-12-03 15:44  少年攻城狮  阅读(0)  评论(0)    收藏  举报

导航