Mybatis 打印完整的SQL

1、

c++ \033输出设置

2、

package org.jeecg.config.mybatis;

import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.ParameterMode;
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.type.TypeHandlerRegistry;
import org.jeecg.common.constant.JeecgConstant;
import org.jeecg.common.util.CN;
import org.springframework.core.Ordered;

import java.lang.reflect.Field;
import java.sql.Statement;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.util.*;

/**
 * <p>
 * 自定义SQL插件,功能如下
 * 1:打印SQL执行时间
 * 2:打印SQL,参数自动设置到SQL中
 * 3:区别慢SQL,SQL执行时间大于5秒的SQL为红色字体,否则为黄色字体,(执行时间可以自定义)
 * </p>
 */
@Intercepts({
        @Signature(type = StatementHandler.class, method = "update", args = {Statement.class}),
        // @Signature(type = StatementHandler.class, method = "query", args = {MappedStatement.class, Statement.class, Object.class, RowBounds.class, ResultHandler.class}),
        // @Signature(type = StatementHandler.class, method = "batch", args = {Statement.class}),
        // @Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class}),
        @Signature(type = StatementHandler.class, method = "query", args = {Statement.class, ResultHandler.class}),

})
@Slf4j
public class MybatisSqlPrintInterceptor implements Interceptor, Ordered {
    private static final Long DEFAULT_TIME_OUT = 5000L;
    private static final ThreadLocal<SimpleDateFormat> SIMPLE_DATE_FORMAT = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"));

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        long startTime = System.currentTimeMillis();
        Object proceed = invocation.proceed();
        printSql(startTime, proceed, invocation);
        return proceed;
    }

    private void printSql(long startTime, Object proceed, Invocation invocation) {
        if (!JeecgConstant.LogClass.getIsPrintSql())
            return;
        try {
            String sql = formatSql(invocation);
            String method = getMethod(invocation);
            long elapsedTime = System.currentTimeMillis() - startTime;
            Integer updateCount = getUpdateCount(proceed, invocation);
            String str = String.format("影响行数(%s),执行时间(%s毫秒),执行方法(%s),[执行SQL]\n%s ", updateCount, elapsedTime, method, getColorString(sql));
            log.info(JeecgConstant.LogClass.logPre + str);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

    private Integer getUpdateCount(Object proceed, Invocation invocation) {
        int rowsAffected = 0;
        if (proceed instanceof ArrayList) {
            List arrayList = (List) proceed; // 将 Object 转换为 ArrayList
            rowsAffected = arrayList.size();
        } else {
            rowsAffected = getRowsAffected(invocation);
        }
        return rowsAffected;
    }

    private Integer getRowsAffected(Invocation invocation) {
        int sa = 0;
        Statement statement = (Statement) invocation.getArgs()[0]; // 获取第一个参数(Statement)
        try {
            int rowsAffected = statement.getUpdateCount(); // 获取受影响的行数
            sa = rowsAffected;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return sa;
    }

    private String getColorString(String str) {
        //[c++ \033输出设置]https://blog.csdn.net/weixin_45469233/article/details/130100885
        return String.format("\033[33;1m%s\033[0m", str);
    }

    @Override
    public void setProperties(Properties properties) {
        System.out.println("插件配置的信息:" + CN.toJSONString(properties));
    }

    /**
     * 格式化SQL及其参数
     */
    private String formatSql(Invocation invocation) throws Exception {
        StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
        ParameterHandler parameterHandler = statementHandler.getParameterHandler();
        BoundSql boundSql = statementHandler.getBoundSql();
        String sql = boundSql.getSql();
        if (CN.isEmpty(sql)) {
            return "";
        }

        Class<? extends ParameterHandler> parameterHandlerClass = parameterHandler.getClass();
        Field mappedStatementField = parameterHandlerClass.getDeclaredField("mappedStatement");
        mappedStatementField.setAccessible(true);
        MappedStatement mappedStatement = (MappedStatement) mappedStatementField.get(parameterHandler);

        // 美化sql
        // sql = beautifySql(sql).toLowerCase();

        // 不传参数的场景,直接把Sql美化一下返回出去
        // Object parameterObject = parameterHandler.getParameterObject();
        // List<ParameterMapping> parameterMappingList = boundSql.getParameterMappings();
        // if (Objects.isNull(parameterObject) || parameterMappingList.isEmpty()) {
        //     return sql;
        // }

        // 定义一个没有替换过占位符的sql,用于出异常时返回
        String sqlWithoutReplacePlaceholder = sql;

        try {
            sql = handleCommonParameter(boundSql, mappedStatement);
        } catch (Exception e) {
            System.err.println(JeecgConstant.LogClass.logPre + "[handleCommonParameter]" + e.getMessage());
            // 占位符替换过程中出现异常,则返回没有替换过占位符但是格式美化过的sql
            return sqlWithoutReplacePlaceholder;
        }
        String sql1 = beautifySql(sql);
        return sql1;
    }

    /**
     * 替换SQL中的?,设置sql参数
     */
    private String handleCommonParameter(BoundSql boundSql, MappedStatement mappedStatement) {
        String sql = boundSql.getSql();
        Object parameterObject = boundSql.getParameterObject();
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
        Configuration configuration = mappedStatement.getConfiguration();
        TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();

        for (ParameterMapping parameterMapping : parameterMappings) {
            if (parameterMapping.getMode() != ParameterMode.OUT) {
                Object value;
                String propertyName = parameterMapping.getProperty();
                if (boundSql.hasAdditionalParameter(propertyName)) {
                    value = boundSql.getAdditionalParameter(propertyName);
                } else if (parameterObject == null) {
                    value = null;
                } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
                    value = parameterObject;
                } else {
                    MetaObject metaObject = configuration.newMetaObject(parameterObject);
                    value = metaObject.getValue(propertyName);
                }
                sql = replacePlaceholder(sql, value);
            }
        }
        return sql;
    }

    /**
     * 根据不同的propertyValue类型,匹配SQL?的类型并替换值
     */
    private String replacePlaceholder(String sql, Object propertyValue) {
        String value;
        if (Objects.nonNull(propertyValue)) {
            if (propertyValue instanceof String) {
                value = "'" + propertyValue + "'";
            } else if (propertyValue instanceof Date) {
                value = "'" + SIMPLE_DATE_FORMAT.get().format(propertyValue) + "'";
            } else if (propertyValue instanceof LocalDate) {
                value = "'" + SIMPLE_DATE_FORMAT.get().format((LocalDate) propertyValue) + "'";
            } else {
                value = propertyValue.toString();
            }
        } else {
            value = "null";
        }
        return sql.replaceFirst("\\?", value);
    }

    /**
     * 根据不同的超时时间打印不同颜色的字体,若超时时间大于默认的超时时间,打印红色字体,否则打印黄色字体
     */
    private void printColorString(String str, Long timeOut) {
        if (timeOut < DEFAULT_TIME_OUT) {
            log.info(JeecgConstant.LogClass.logPre + "\033[33;4m" + str + "\033[0m");
        } else {
            log.info(JeecgConstant.LogClass.logPre + "\033[33;4m" + str + "\033[0m");
        }
    }

    private String beautifySql(String sql) {
        sql = sql.replaceAll("[\\s\n ]+", " ");
        return sql;
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    private String getMethod(Invocation invocation) throws Exception {
        StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
        ParameterHandler parameterHandler = statementHandler.getParameterHandler();
        // BoundSql boundSql = statementHandler.getBoundSql();

        Class<? extends ParameterHandler> parameterHandlerClass = parameterHandler.getClass();
        Field mappedStatementField = parameterHandlerClass.getDeclaredField("mappedStatement");
        mappedStatementField.setAccessible(true);
        MappedStatement mappedStatement = (MappedStatement) mappedStatementField.get(parameterHandler);
        return mappedStatement.getId();
    }

    @Override
    public int getOrder() {
        return Ordered.HIGHEST_PRECEDENCE;
    }
}
posted @ 2024-04-02 15:25  一只桔子2233  阅读(255)  评论(0编辑  收藏  举报