如何获取MyBatis Plus执行的完整的SQL语句

 更新时间:2024年07月17日 10:50:15   作者:梁云亮  
这篇文章主要介绍了如何获取MyBatis Plus执行的完整的SQL语句问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

注意:本示例介绍的方法仅支持MyBatis Plus

原理

自定义插件,将SQL语句中的?替换成具体的参数值。

实现

自定义插件

import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import com.tiku.utils.LogUtil;
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.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.sql.SQLException;
import java.text.DateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.regex.Matcher;

public class MyBatisPlusSqlLogInterceptor implements InnerInterceptor {
    private static boolean printSQL = true;

    @Override
    public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        if (printSQL) {
            MappedStatement mappedStatement = ms;
            String sqlId = mappedStatement.getId();
            Configuration configuration = mappedStatement.getConfiguration();
            String sql = getSql(configuration, boundSql, sqlId);

            final String ip = RequestUtil.getIp();
            final String requestType = RequestUtil.getMethod();
            final String url = RequestUtil.getRequestUrl();
            final String parameters = RequestUtil.getParameters();

            String userAgent = RequestUtil.getUserAgent();
            UserAgent agent = UserAgent.parseUserAgentString(userAgent);
            final String browser = agent.getBrowser().getName();
            final String os = agent.getOperatingSystem().getName();

            LogUtil.println("用户访问了:{},浏览器是:{},操作系统是:{},IP是:{},请求方式是:{},请求参数是:{},SQL语句是:{}", url, browser, os, ip, requestType, parameters, sql);
        }
    }

    @Override
    public void beforeUpdate(Executor executor, MappedStatement ms, Object parameter) throws SQLException {
        if (printSQL) {
            MappedStatement mappedStatement = ms;
            String sqlId = mappedStatement.getId();
            Configuration configuration = mappedStatement.getConfiguration();
            BoundSql boundSql = mappedStatement.getBoundSql(parameter);
            String sql = getSql(configuration, boundSql, sqlId);

            final String ip = RequestUtil.getIp();
            final String requestType = RequestUtil.getMethod();
            final String url = RequestUtil.getRequestUrl();
            final String parameters = RequestUtil.getParameters();

            String userAgent = RequestUtil.getUserAgent();
            UserAgent agent = UserAgent.parseUserAgentString(userAgent);
            final String browser = agent.getBrowser().getName();
            final String os = agent.getOperatingSystem().getName();

            LogUtil.println("用户访问了:{},浏览器是:{},操作系统是:{},IP是:{},请求方式是:{},请求参数是:{},SQL语句是:{}", url, browser, os, ip, requestType, parameters, sql);
        }
    }

    private String getSql(Configuration configuration, BoundSql boundSql, String sqlId) {
        try {
            String sql = showSql(configuration, boundSql);
            StringBuilder str = new StringBuilder(100);
            str.append(sqlId);
            str.append(" ==> ");
            str.append(sql);
            str.append(";");
            return str.toString();
        } catch (Error e) {
            LogUtil.error("解析 sql 异常", e);
        }
        return "";
    }

    private String showSql(Configuration configuration, BoundSql boundSql) {
        Object parameterObject = boundSql.getParameterObject();
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
        String sql = boundSql.getSql().replaceAll("[\\s]+", " ");
        if (parameterMappings != null && parameterMappings.size() > 0 && parameterObject != null) {
            TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
            if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
                sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(getParameterValue(parameterObject)));
            } else {
                MetaObject metaObject = configuration.newMetaObject(parameterObject);
                for (ParameterMapping parameterMapping : parameterMappings) {
                    String propertyName = parameterMapping.getProperty();
                    if (metaObject.hasGetter(propertyName)) {
                        Object obj = metaObject.getValue(propertyName);
                        sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(getParameterValue(obj)));
                    } else if (boundSql.hasAdditionalParameter(propertyName)) {
                        Object obj = boundSql.getAdditionalParameter(propertyName);
                        sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(getParameterValue(obj)));
                    }
                }
            }
        }
        return sql;
    }

    private String getParameterValue(Object obj) {
        String value = null;
        if (obj instanceof String) {
            value = "'" + obj.toString() + "'";
        } else if (obj instanceof Date) {
            DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.CHINA);
            value = "'" + formatter.format(obj) + "'";
        } else if (obj instanceof LocalDate) {
            value = "'" + ((LocalDate) obj).format(DateTimeFormatter.ofPattern("yyyy-MM-dd")) + "'";
        } else if (obj instanceof LocalDateTime) {
            value = "'" + ((LocalDateTime) obj).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) + "'";

        } else {
            if (obj != null) {
                value = obj.toString();
            } else {
                value = "";
            }
        }
        return value;
    }
}

上面代码用到的两个工具类:

  • RequestUtil.java
import com.tiku.enums.ResultEnum;
import com.tiku.ex.GlobalException;
import jakarta.servlet.http.HttpServletRequest;

import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 * @author hc
 */
public class RequestUtil {
    /**
     * 私有构造方法,防止实例化
     */
    private RequestUtil() {

    }

    /**
     * 获取所有的请求参数及值
     *
     * @return eg:pageNum=3&pageSize=5&state=0
     */
    public static String getParameters() {
        HttpServletRequest request = SpringContextUtil.getRequest();
        if (null == request) {
            return null;
        }
        Enumeration<String> paraNames = request.getParameterNames();
        if (paraNames == null) {
            return null;
        }
        StringBuilder sb = new StringBuilder();
        while (paraNames.hasMoreElements()) {
            String paraName = paraNames.nextElement();
            sb.append("&").append(paraName).append("=").append(request.getParameter(paraName));
        }
        return sb.toString();
    }

    public static Map<String, String> getParameterMap(HttpServletRequest request) {
        // 参数Map
        Map<String, String[]> properties = request.getParameterMap();
        // 返回值Map
        Map<String, String> returnMap = new HashMap<>(16);
        Iterator<Map.Entry<String, String[]>> entries = properties.entrySet().iterator();
        Map.Entry<String, String[]> entry;
        String name;
        String value = "";
        while (entries.hasNext()) {
            entry = entries.next();
            name = entry.getKey();
            Object valueObj = entry.getValue();
            if (null == valueObj) {
                value = "";
            } else {
                String[] values = (String[]) valueObj;
                for (String value1 : values) {
                    value = value1 + ",";
                }
                value = value.substring(0, value.length() - 1);
            }
            returnMap.put(name, value);
        }
        return returnMap;
    }

    /**
     * 获取用户所有的请求参数,以Map的形式返回
     *
     * @return
     */
    public static Map<String, String[]> getParametersMap() {
        HttpServletRequest request = SpringContextUtil.getRequest();
        if (null == request) {
            return new HashMap<>();
        }
        return request.getParameterMap();
    }

    /**
     * 获取请求头中指定名称的值
     *
     * @param name
     * @return
     */
    public static String getHeader(String name) {
        HttpServletRequest request = SpringContextUtil.getRequest();
        if (null == request) {
            return null;
        }
        return request.getHeader(name);
    }

    public static String getReferer() {
        return getHeader("Referer");
    }

    /**
     * 获取用户代理对象
     *
     * @return
     */
    public static String getUserAgent() {
        return getHeader("User-Agent");
    }

    /**
     * 获取请求用户的IP地址
     *
     * @return
     */
    public static String getIp() {
        HttpServletRequest request = SpringContextUtil.getRequest();
        if (null == request) {
            return null;
        }
        return IPUtil.getIpAddr(request);
    }

    /**
     * 获取用户请求的Controller的路径
     *
     * @return 比如:http://localhost/list
     */
    public static String getRequestUrl() {
        HttpServletRequest request = SpringContextUtil.getRequest();
        if (null == request) {
            return null;
        }
        return request.getRequestURL().toString();
    }

    /**
     * 获取URI
     *
     * @return
     */
    public static String getRequestUri() {
        HttpServletRequest request = SpringContextUtil.getRequest();
        if (null == request) {
            return null;
        }
        return request.getRequestURI();
    }

    /**
     * 获取用户请求的方式,值有: POST GET 3 PUT DELETE
     *
     * @return
     */
    public static String getMethod() {
        HttpServletRequest request = SpringContextUtil.getRequest();
        if (null == request) {
            return null;
        }
        return request.getMethod();
    }

    /**
     * 判断用户的请求是否是AJAX请求
     *
     * @param request
     * @return
     */
    public static boolean isAjax(HttpServletRequest request) {
        if (null == request) {
            request = SpringContextUtil.getRequest();
        }
        if (null == request) {
            return false;
        }
        // 解析原错误信息,封装后返回,此处返回非法的字段名称,原始值,错误信息
        // 使用HttpServletRequest中的header检测请求是否为ajax, 如果是ajax则返回json, 如果为非ajax则返回view(即ModelAndView)
        String contentTypeHeader = request.getHeader("Content-Type");
        String acceptHeader = request.getHeader("Accept");
        String xRequestedWith = request.getHeader("X-Requested-With");
        return (contentTypeHeader != null && contentTypeHeader.contains("application/json"))
                || (acceptHeader != null && acceptHeader.contains("application/json"))
                || "XMLHttpRequest".equalsIgnoreCase(xRequestedWith);
    }

    /**
     * 从HttpServletReqeust对象中获取key对应的值 先从Reqeust Parameter中获取,如果没有再从Header中获取
     *
     * @param key
     * @return
     */
    public static String getValueFromRequest(String key) {
        HttpServletRequest request = SpringContextUtil.getRequest();
        return getValueFromRequest(request, key);
    }

    /**
     * 从HttpServletReqeust对象中获取key对应的值 先从Reqeust Parameter中获取,如果没有再从Header中获取
     *
     * @param request
     * @param key
     * @return
     */
    public static String getValueFromRequest(HttpServletRequest request, String key) {
        String token = request.getParameter(key);
        // 从header中获取值
        if (token == null) {
            token = request.getHeader(key);
        }
        if (token == null) {
            throw new GlobalException(ResultEnum.TOKEN_INVALID);
        }
        return token;
    }
}
  • UserAgent

该工具类是第三方jar包中定义的:UserAgentUtils-1.21.jar

注册插件

@Configuration
public class MyBatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();

        interceptor.addInnerInterceptor(new MyBatisPlusSqlLogInterceptor());
        return interceptor;
    }

}

这样在执行CRUD操作数据时,就会在控制台中输出相应的完整的SQL语句

比如:

2024-05-20 07:46:22.365 [INFO ] [] 6868 --  SqlLogInterceptor.beforeQuery() Line: 48 Line:100 :  用户访问了:http://127.0.0.1:8081/tiku/user/v1/login,浏览器是:Chrome 12,操作系统是:Windows 10,IP是:127.0.0.1,请求方式是:POST,请求参数是:,SQL语句是:com.tiku.mapper.UserDetailsMapper.selectById ==> SELECT id,num,nickname,`name`,avatar,gender,birth,email,tel,qq,wechat,credit,interest,country_id,addr,verify_time,login_time,login_ip,login_addr,creator_id,updater_id,info,priority,create_time,update_time FROM sys_user_details WHERE id=1;

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • 使用Spring Security OAuth2实现单点登录

    使用Spring Security OAuth2实现单点登录

    在本教程中,我们将讨论如何使用Spring Security OAuth和Spring Boot实现SSO - 单点登录。感兴趣的朋友跟随小编一起看看吧
    2019-06-06
  • IDEA中将SpringBoot项目提交到git仓库的方法步骤

    IDEA中将SpringBoot项目提交到git仓库的方法步骤

    本文主要介绍了IDEA中将SpringBoot项目提交到git仓库的方法步骤,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-12-12
  • JeecgBoot页面Online报表配置过程(动态)

    JeecgBoot页面Online报表配置过程(动态)

    文章介绍了如何根据登录者角色信息动态配置报表,并详细说明了Online报表的配置步骤,包括数据源选择、字段显示、查询条件设置等,同时,文章还涉及了页面代码部分,包括依赖引入、用户信息获取、参数配置和form表单配置等
    2026-01-01
  • springboot @ConditionalOnMissingBean注解的作用详解

    springboot @ConditionalOnMissingBean注解的作用详解

    这篇文章主要介绍了springboot @ConditionalOnMissingBean注解的作用详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-08-08
  • uploadify上传及后台文件合法性验证的代码解析

    uploadify上传及后台文件合法性验证的代码解析

    这篇文章主要介绍了uploadify上传及后台文件合法性验证的代码解析,整段代码分为后台上传方法,文件合法性验证类,前端上传js,非常不错具有参考借鉴价值,需要的朋友可以参考下
    2016-11-11
  • 基于SpringBoot + Android实现登录功能

    基于SpringBoot + Android实现登录功能

    在移动互联网的今天,许多应用需要通过移动端实现与服务器的交互功能,其中登录是最常见且基础的一种功能,本篇博客将详细介绍如何使用 Spring Boot 和 Android 实现一个完整的登录功能,需要的朋友可以参考下
    2024-11-11
  • IDEA mybatis Mapper.xml报红的最新解决办法

    IDEA mybatis Mapper.xml报红的最新解决办法

    这篇文章主要介绍了IDEA mybatis Mapper.xml报红的解决办法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-04-04
  • 详解Java设计模式编程中的里氏替换原则

    详解Java设计模式编程中的里氏替换原则

    这篇文章主要介绍了Java设计模式编程中的里氏替换原则,有这个名字是因为这是由麻省理工学院的一位姓里的女士Barbara Liskov提出来的(嗯...),需要的朋友可以参考下
    2016-02-02
  • Java Random 随机数的用法小结

    Java Random 随机数的用法小结

    本文主要介绍了Java Random随机数的用法小结,Random类功能全面,支持生成整数、浮点数、布尔值及实现洗牌等高级操作,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来一起学习学习吧
    2025-07-07
  • 深入谈谈java的枚举(enum)类型

    深入谈谈java的枚举(enum)类型

    这篇文章介绍的是java中的枚举类型,对于枚举类型的相关知识,我们也介绍过不少方面的内容。希望本文能够给你带来帮助,下面来一起看看,有需要的可以参考借鉴。
    2016-09-09

最新评论