Mybatis-Plus系列---【自定义拦截器实现sql完整拼接及耗时打印】

1.自定义sql拦截器(注意:Executor是org.apache.ibatis.executor.Executor包下的,不要导错了)

java 复制代码
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.注册拦截器

java 复制代码
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.配置文件配置开启(可以不配置,不配的话默认关闭)

properties 复制代码
mybatis-plus.sql-log.enabled = true
相关推荐
小陈工2 小时前
Python Web开发入门(十七):Vue.js与Python后端集成——让前后端真正“握手言和“
开发语言·前端·javascript·数据库·vue.js·人工智能·python
科技小花7 小时前
数据治理平台架构演进观察:AI原生设计如何重构企业数据管理范式
数据库·重构·架构·数据治理·ai-native·ai原生
一江寒逸7 小时前
零基础从入门到精通MySQL(中篇):进阶篇——吃透多表查询、事务核心与高级特性,搞定复杂业务SQL
数据库·sql·mysql
凯尔萨厮7 小时前
创建SpringWeb项目(Spring2.0)
spring·mvc·mybatis
D4c-lovetrain7 小时前
linux个人心得22 (mysql)
数据库·mysql
阿里小阿希8 小时前
CentOS7 PostgreSQL 9.2 升级到 15 完整教程
数据库·postgresql
荒川之神8 小时前
Oracle 数据仓库雪花模型设计(完整实战方案)
数据库·数据仓库·oracle
做个文艺程序员8 小时前
MySQL安全加固十大硬核操作
数据库·mysql·安全
不吃香菜学java8 小时前
Redis简单应用
数据库·spring boot·tomcat·maven
一个天蝎座 白勺 程序猿8 小时前
Apache IoTDB(15):IoTDB查询写回(INTO子句)深度解析——从语法到实战的ETL全链路指南
数据库·apache·etl·iotdb