# Use MyBatis interceptor to achieve complete output of Mybatis sql console

Use MyBatis interceptor to achieve complete output of Mybatis sql console

Mybatis interceptor

Introduction

  • As the name implies, the interceptor must intercept a certain request or interface to perform certain operations. For example, we can implement the HandlerInterceptor interface, which can intercept the web request interface to verify whether the user is logged in or not.

  • MyBatis allows you to intercept calls at a certain point during the execution of the mapped statement. By default, MyBatis allows the use of plug-ins to intercept method calls including:

  1. Method of intercepting the executor: Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
  2. Intercept parameter processing: ParameterHandler (getParameterObject, setParameters)
  3. Intercept the processing of the result set: ResultSetHandler (handleResultSets, handleOutputParameters)
  4. Interception of Sql syntax construction: StatementHandler (prepare, parameterize, batch, update, query)

Source code preview

Interceptor:

Mybatis does not have a default implementation of this interface, you can implement it according to your needs when you use it. The paging query tool pagehelper has an implementation of the Interceptor interface to assist in the realization of paging query.

public interface Interceptor {
    
    
    Object intercept(Invocation var1) throws Throwable;

    default Object plugin(Object target) {
    
    
        return Plugin.wrap(target, this);
    }

    default void setProperties(Properties properties) {
    
    
    }
}
Official website configuration interceptor instance
  • ExamplePlugin: All update methods that execute the executor will be intercepted by this interceptor.
@Intercepts({
    
    @Signature(
  type= Executor.class,
  method = "update",
  args = {
    
    MappedStatement.class,Object.class})})
public class ExamplePlugin implements Interceptor {
    
    
  public Object intercept(Invocation invocation) throws Throwable {
    
    
    return invocation.proceed();
  }
  public Object plugin(Object target) {
    
    
    return Plugin.wrap(target, this);
  }
  public void setProperties(Properties properties) {
    
    
  }
}
  • xml placement
<plugins>
    <plugin interceptor="org.format.mybatis.cache.interceptor.ExamplePlugin"></plugin>
</plugins>
  • Object intercept (Invocation invocation) is the place where the interception logic is implemented. Internally, invocation.proceed() is used to explicitly advance the chain of responsibility, that is, to call the next interceptor to intercept the target method.

  • Object plugin (Object target) is to use the current interceptor to generate a proxy to the target target, which is actually done through Plugin.wrap(target, this), passing the target target and the interceptor this to the wrapper function.

  • setProperties(Properties properties) is used to set additional parameters, which are configured in the Properties node of the interceptor.

    The annotation describes the signature [type, method, args] that specifies the interception method (that is, which method of which object is intercepted), which is used for decision before interception.

Plugin.wrap method
public staticObject wrap(Object target, Interceptor interceptor) {
    
    
    //从拦截器的注解中获取拦截的类名和方法信息
    Map<Class<?>, Set<Method>> signatureMap =getSignatureMap(interceptor);
    Class<?> type = target.getClass();
    //解析被拦截对象的所有接口(注意是接口)
    Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
    if(interfaces.length > 0) {
    
    
        //生成代理对象, Plugin对象为该代理对象的InvocationHandler  
        returnProxy.newProxyInstance(type.getClassLoader(), interfaces, new Plugin(target,interceptor,signatureMap));
    }
    returntarget;
} 

Console Sql complete output realization

  • SqlLoogerConfig
/**
 * @Description:Sql 完整输出sql
 * @Author:LiDong
 * @Create:2020/12/26
 * @Version:1.0.0
 */
@Configuration
public class SqlLoogerConfig {
    
    

    @Bean
    public SqlInterceptor sqlInterceptor() {
    
    
        return new SqlInterceptor();
    }
}
  • SqlInterceptor
package com.li.core.config.mybatis;

import com.github.pagehelper.util.StringUtil;
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.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.session.RowBounds;
import org.apache.ibatis.type.TypeHandlerRegistry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Properties;

/**
 * @Description:mybatis sql拦截器 控制打印出完整sql
 * @Author:LiDong
 * @Create:2020/12/26
 * @Version:1.0.0
 */
@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})}
)
public class SqlInterceptor implements Interceptor {
    
    

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

    private static final ThreadLocal<SimpleDateFormat> DATETIME_FORMATTER = new ThreadLocal<SimpleDateFormat>() {
    
    
        @Override
        protected SimpleDateFormat initialValue() {
    
    
            return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        }
    };

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
    
    
        Object result = null;
        //捕获掉异常,不要影响业务
        try {
    
    
            MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
            Object parameter = null;
            if (invocation.getArgs().length > 1) {
    
    
                parameter = invocation.getArgs()[1];
            }
            String sqlId = mappedStatement.getId();
            BoundSql boundSql = mappedStatement.getBoundSql(parameter);
            Configuration configuration = mappedStatement.getConfiguration();

            long startTime = System.currentTimeMillis();

            try {
    
    
                result = invocation.proceed();
            } finally {
    
    
                long endTime = System.currentTimeMillis();
                long sqlCostTime = endTime - startTime;
                String sql = this.getSql(configuration, boundSql);
                this.formatSqlLog(sqlId, sql, sqlCostTime, result);
            }
            return result;

        } catch (Exception e) {
    
    
            return result;
        }
    }

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

    @Override
    public void setProperties(Properties properties) {
    
    
    }

    /**
     * 获取完整的sql语句
     *
     * @param configuration
     * @param boundSql
     * @return
     */
    private String getSql(Configuration configuration, BoundSql boundSql) {
    
    
        // 输入sql字符串空判断
        String sql = boundSql.getSql();
        if (StringUtil.isEmpty(sql)) {
    
    
            return "";
        }
        return formatSql(sql, configuration, boundSql);
    }

    /**
     * 将占位符替换成参数值
     *
     * @param sql
     * @param configuration
     * @param boundSql
     * @return
     */
    private String formatSql(String sql, Configuration configuration, BoundSql boundSql) {
    
    
        //美化sql
        sql = beautifySql(sql);
        //填充占位符, 目前基本不用mybatis存储过程调用,故此处不做考虑
        Object parameterObject = boundSql.getParameterObject();
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
        TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
        List<String> parameters = new ArrayList<>();
        if (parameterMappings != null) {
    
    
            MetaObject metaObject = parameterObject == null ? null : configuration.newMetaObject(parameterObject);
            for (int i = 0; i < parameterMappings.size(); i++) {
    
    
                ParameterMapping parameterMapping = parameterMappings.get(i);
                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 {
    
    
                        value = metaObject == null ? null : metaObject.getValue(propertyName);
                    }

                    if (value instanceof Number) {
    
    
                        parameters.add(String.valueOf(value));
                    } else {
    
    
                        StringBuilder builder = new StringBuilder();
                        builder.append("'");
                        if (value instanceof Date) {
    
    
                            builder.append(DATETIME_FORMATTER.get().format((Date) value));
                        } else if (value instanceof String) {
    
    
                            builder.append(value);
                        }
                        builder.append("'");
                        parameters.add(builder.toString());
                    }
                }
            }
        }

        for (String value : parameters) {
    
    
            sql = sql.replaceFirst("\\?", value);
        }
        return sql;
    }


    /**
     * 格式化sql日志
     *
     * @param sqlId
     * @param sql
     * @param costTime
     * @return
     */
    private void formatSqlLog(String sqlId, String sql, long costTime, Object obj) {
    
    
        String sqlLog = "=====> " + sql;
        StringBuffer result = new StringBuffer();
        if (obj instanceof List) {
    
    
            List list = (List) obj;
            int count = list.size();
            result.append("=====> Total:" + count);
        } else if (obj instanceof Integer) {
    
    
            result.append("=====> Total:" + obj);
        }
        result.append(" SpendTime:" + costTime + " ms");
        logger.info("\n------------------------------------------------------------------------------------------------------------------\n"
                + sqlLog + "\n" + result +
                "\n------------------------------------------------------------------------------------------------------------------");
    }


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

Achieve effect

Insert picture description here

Guess you like

Origin blog.csdn.net/qq_37248504/article/details/111767707