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();
        }
    }
}
相关推荐
誓约酱1 小时前
Linux下ext2文件系统
android·linux·c语言·数据库·c++·后端·ubuntu
橘子师兄1 小时前
mysql本地安装和pycharm链接数据库操作
数据库·mysql·pycharm
云道轩1 小时前
openGauss 创建数据库
数据库·sql·oracle
秋夜白2 小时前
【LevelDB 和 Sqlite】
数据库·sqlite
Whacky-u3 小时前
Hive SQL必刷练习题:连续问题 & 间断连续
大数据·数据库·数据仓库·sql
Lang_xi_3 小时前
汇总统计数据--SQL中聚集函数的使用
数据库·mysql
全镇人的希望3 小时前
oarcle执行报错提示:SQL 错误 [1839] [22008]: ORA-01839: 指定月份的日期无效问题解决
数据库·sql
百香果果ccc3 小时前
SQL中的公用表表达式
数据库·sql
徐礼昭|商派软件市场负责人4 小时前
支持各大平台账单处理,支持复杂业财数据的精细化对账|商派OMS
大数据·数据库·人工智能·oms·财务对账