SQL美化器优化

文章目录

1.目录

2.代码

java 复制代码
package com.sunxiansheng.mybatis.plus.inteceptor;

import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.*;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.*;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.type.TypeHandlerRegistry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.github.vertical_blank.sqlformatter.SqlFormatter;
import com.github.vertical_blank.sqlformatter.languages.Dialect;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

import java.sql.Statement;
import java.util.List;
import java.util.Properties;
import java.util.regex.Matcher;

/**
 * SQL美化拦截器:显示完整的SQL,并输出执行结果
 */
@Intercepts({
        @Signature(type = StatementHandler.class, method = "query", args = {Statement.class, ResultHandler.class}),
        @Signature(type = StatementHandler.class, method = "update", args = {Statement.class}),
        @Signature(type = StatementHandler.class, method = "batch", args = {Statement.class})
})
public class SqlBeautyInterceptor implements Interceptor {

    private static final Logger logger = LoggerFactory.getLogger(SqlBeautyInterceptor.class);

    // ANSI 颜色代码
    private static final String ANSI_RESET = "\u001B[0m";
    private static final String ANSI_BRIGHT_GREEN = "\u001B[92m";   // 亮绿色(结果)
    private static final String ANSI_BRIGHT_BLUE = "\u001B[94m";    // 亮蓝色(项目名称)

    // Gson 实例,用于格式化结果
    private static final Gson GSON = new GsonBuilder()
            .setPrettyPrinting()
            .disableHtmlEscaping()
            .create();

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
        MetaObject metaObject = SystemMetaObject.forObject(statementHandler);

        // 分离代理对象链
        while (metaObject.hasGetter("h")) {
            Object object = metaObject.getValue("h");
            metaObject = SystemMetaObject.forObject(object);
        }
        // 分离最后一个代理对象的目标类
        while (metaObject.hasGetter("target")) {
            Object object = metaObject.getValue("target");
            metaObject = SystemMetaObject.forObject(object);
        }

        MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
        BoundSql boundSql = (BoundSql) metaObject.getValue("delegate.boundSql");
        Configuration configuration = mappedStatement.getConfiguration();

        long startTime = System.currentTimeMillis();
        Object result = null;
        try {
            // 执行 SQL,获取结果
            result = invocation.proceed();
            return result;
        } finally {
            long endTime = System.currentTimeMillis();
            long sqlCost = endTime - startTime;
            SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType();

            // 使用修改后的 formatSql 方法
            String formattedSql = formatSql(configuration, boundSql);

            // 格式化执行结果
            String formattedResult = formatResult(result, sqlCommandType);

            // 日志输出
            logger.info("\n========================\n{}SQL 类型:{} {}{}{}\n{}SQL:{}\n{}{}{}\n{}执行耗时:{} {}{}ms{} \n{}结果:{}\n{}{}\n{}========================{}",
                    ANSI_BRIGHT_BLUE, ANSI_RESET,
                    ANSI_BRIGHT_GREEN, sqlCommandType, ANSI_RESET,
                    ANSI_BRIGHT_BLUE, ANSI_RESET,
                    ANSI_BRIGHT_GREEN, formattedSql, ANSI_RESET,
                    ANSI_BRIGHT_BLUE, ANSI_RESET,
                    ANSI_BRIGHT_GREEN, sqlCost, ANSI_RESET,
                    ANSI_BRIGHT_BLUE, ANSI_RESET,
                    ANSI_BRIGHT_GREEN, formattedResult, ANSI_RESET,
                    ANSI_BRIGHT_BLUE, ANSI_RESET);
        }
    }

    @Override
    public Object plugin(Object target) {
        // 使用 Plugin.wrap 生成代理对象
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {
        // 可以通过配置文件设置属性
    }

    /**
     * 格式化 SQL,将参数替换到 SQL 中,并使用 SQLFormatter 进行格式化
     *
     * @param configuration MyBatis 配置对象
     * @param boundSql      绑定的 SQL
     * @return 格式化后的 SQL
     */
    private String formatSql(Configuration configuration, BoundSql boundSql) {
        try {
            // 获取 SQL 语句
            String sql = boundSql.getSql();
            // 获取参数对象
            Object parameterObject = boundSql.getParameterObject();
            // 获取参数映射
            List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();

            if (parameterMappings != null && !parameterMappings.isEmpty() && parameterObject != null) {
                TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
                MetaObject metaObject = configuration.newMetaObject(parameterObject);

                // 替换 SQL 中的参数占位符
                for (ParameterMapping parameterMapping : parameterMappings) {
                    String propertyName = parameterMapping.getProperty();
                    Object value;

                    if (boundSql.hasAdditionalParameter(propertyName)) {
                        value = boundSql.getAdditionalParameter(propertyName);
                    } else if (metaObject.hasGetter(propertyName)) {
                        value = metaObject.getValue(propertyName);
                    } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
                        value = parameterObject;
                    } else {
                        value = null;
                    }

                    sql = replaceFirstQuestionMark(sql, getParameterValue(value));
                }
            }

            // 使用 SQLFormatter 格式化 SQL
            return SqlFormatter.of(Dialect.MySql).format(sql);
        } catch (Exception e) {
            logger.error("Error formatting SQL: ", e);
            return boundSql.getSql();
        }
    }

    private String replaceFirstQuestionMark(String sql, String value) {
        return sql.replaceFirst("\\?", Matcher.quoteReplacement(value));
    }

    private String getParameterValue(Object obj) {
        if (obj == null) {
            return "null";
        }
        if (obj instanceof String) {
            return "'" + obj + "'";
        }
        if (obj instanceof Number) {
            return obj.toString();
        }
        if (obj instanceof java.util.Date || obj instanceof java.sql.Date || obj instanceof java.sql.Timestamp) {
            return "'" + obj.toString() + "'";
        }
        return "'" + obj.toString() + "'";
    }

    /**
     * 格式化 SQL 执行结果
     *
     * @param result         执行结果
     * @param sqlCommandType SQL 类型
     * @return 格式化后的结果字符串
     */
    private String formatResult(Object result, SqlCommandType sqlCommandType) {
        if (result == null) {
            return "null";
        }
        try {
            String formattedResult;
            if (sqlCommandType == SqlCommandType.SELECT) {
                // 查询语句,结果通常是 List
                formattedResult = GSON.toJson(result);
            } else if (sqlCommandType == SqlCommandType.UPDATE || sqlCommandType == SqlCommandType.DELETE || sqlCommandType == SqlCommandType.INSERT) {
                // 更新语句,结果是受影响的行数
                formattedResult = result.toString() + " 行受影响";
            } else {
                // 其他类型
                formattedResult = result.toString();
            }

            // 可根据需要限制结果输出长度,避免日志过大
            int maxLength = 2000;
            if (formattedResult.length() > maxLength) {
                formattedResult = formattedResult.substring(0, maxLength) + "... [结果过长,已截断]";
            }
            return formattedResult;
        } catch (Exception e) {
            logger.error("Error formatting result: ", e);
            return result.toString();
        }
    }
}
相关推荐
皙然6 分钟前
Redis八大核心数据类型详解:从底层实现到实战落地
数据库·redis·bootstrap
时光追逐者40 分钟前
一款免费、简单、高效的在线数据库设计工具
数据库·mysql·oracle·sql server
another heaven41 分钟前
【软考 2026 最新版 NoSQL 数据库全分类】
数据库·nosql
满天星830357743 分钟前
【MySQL】表的操作
linux·服务器·数据库·mysql
yashuk1 小时前
Ubuntu 系统下安装 Nginx
数据库·nginx·ubuntu
F1FJJ1 小时前
VS Code 里管理 PostgreSQL,有哪些选择?主流扩展横向对比
网络·数据库·postgresql·容器
Bdygsl1 小时前
MySQL(8)—— 事务
数据库·mysql
IvorySQL1 小时前
直播回顾| PostgreSQL 18.3 x IvorySQL 5.3:开启 AI 数据库新纪元
数据库·postgresql·开源
编程之升级打怪1 小时前
数据库的实时同步和异步同步
数据库
captain3761 小时前
MySQL增删改查
数据库·mysql