MyBatis/MyBatis Plus打印的SQL调试起来比较麻烦

当然IDEA/eclipse都有类似mybatis log plugin这种插件来解析, 但是安装插件有些许弊端, 就写了个工具类处理

如果需要更详细的SQL分析, 建议使用p6spy. 本配置仅仅是为了调试SQL方便

效果展示:

代码:

github

package kim.nzxy.ly.common.interceptor;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.reflect.FieldUtils;
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.ParameterMapping;
import org.apache.ibatis.mapping.ParameterMode;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
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.springframework.context.annotation.Profile;
import org.springframework.stereotype.Component; import java.sql.Statement;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors; /**
* mybatis sql 日志拦截器, 用于打印SQL
*
* @author ly-chn
*/
@Slf4j
@Profile("local")
@Component
@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 MybatisLogSqlInterceptor implements Interceptor {
private static final Set<String> NEED_BRACKETS =
Set.of("String", "Date", "Time", "LocalDate", "LocalTime", "LocalDateTime", "BigDecimal", "Timestamp"); private Configuration configuration = null; @Override
public Object intercept(Invocation invocation) throws Throwable {
Object target = invocation.getTarget();
long startTime = System.currentTimeMillis();
try {
// 如需打印结果, 返回值即结果集
return invocation.proceed();
} finally {
long sqlCost = System.currentTimeMillis() - startTime;
String sql = this.getSql(target);
log.info("sql took {}ms: {}", sqlCost, sql);
}
} /**
* 获取sql
*/
private String getSql(Object target) {
try {
StatementHandler statementHandler = (StatementHandler) target;
BoundSql boundSql = statementHandler.getBoundSql();
if (configuration == null) {
final ParameterHandler parameterHandler = statementHandler.getParameterHandler();
this.configuration = (Configuration) FieldUtils.readField(parameterHandler, "configuration", true);
}
// 替换参数格式化Sql语句,去除换行符
return formatSql(boundSql, configuration);
} catch (Exception e) {
log.warn("get sql error {}", target, e);
return "failed to parse sql";
}
} /**
* 获取完整的sql实体的信息
*/
private String formatSql(BoundSql boundSql, Configuration configuration) {
String sql = boundSql.getSql();
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
Object parameterObject = boundSql.getParameterObject();
// 输入sql字符串空判断
if (StringUtils.isEmpty(sql) || Objects.isNull(configuration)) {
return "";
} TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry(); // 替换空格容易造成本身存在空格的查询条件被替换
sql = sql.replaceAll("[\n\r ]+", " "); if (parameterMappings == null) {
return sql;
} parameterMappings = parameterMappings.stream().filter(it -> it.getMode() != ParameterMode.OUT).collect(Collectors.toList()); final StringBuilder result = new StringBuilder(sql); // 解析问号并填充
for (int i = result.length(); i > 0; i--) {
if (result.charAt(i - 1) != '?') {
continue;
}
ParameterMapping parameterMapping = parameterMappings.get(parameterMappings.size() - 1);
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);
}
if (value != null) {
String type = value.getClass().getSimpleName();
if (NEED_BRACKETS.contains(type)) {
result.replace(i - 1, i, "'" + value + "'");
} else {
result.replace(i - 1, i, value.toString());
}
} else {
result.replace(i - 1, i, "null");
}
parameterMappings.remove(parameterMappings.size() - 1);
}
return result.toString();
}
}

MyBatis(Plus) 打印SQL, 分析执行时间的更多相关文章

  1. MyBatis 插件 : 打印 SQL 及其执行时间

    Plugins 摘一段来自MyBatis官方文档的文字. MyBatis允许你在某一点拦截已映射语句执行的调用.默认情况下,MyBatis允许使用插件来拦截方法调用: Executor(update. ...

  2. mybatis日志,打印sql语句,输出sql

    mybatis日志,打印sql语句,输出sql<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE ...

  3. mybatis配置打印sql

    mybatis配置打印sql: <settings> <setting name="logImpl" value="STDOUT_LOGGING&quo ...

  4. Springboot中mybatis控制台打印sql语句

    Springboot中mybatis控制台打印sql语句 https://www.jianshu.com/p/3cfe5f6e9174 https://www.jianshu.com/go-wild? ...

  5. Springboot自定义starter打印sql及其执行时间

    前面写到了通过实现mybatis提供的org.apache.ibatis.plugin.Interceptor接口实现了打印SQL执行时间,并格式化SQL及其参数,如果我们使用的是ssm还得再配置文件 ...

  6. mybatis 控制台打印sql

    开发时调试使用 <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBe ...

  7. mybatis 控制台打印sql语句

    其实很简单,打印SQL只需要加一个setting就可以了.亲测可用. mybatis-config.xml: <settings>        <setting name=&quo ...

  8. spring-mvc Mybatis插件打印SQL

    代码: package com.chainup.exchange.service.adapter; import com.chainup.exchange.service.impl.AccountSe ...

  9. mybatis logback打印sql

    <?xml version="1.0" encoding="UTF-8" ?><configuration> <contextNa ...

  10. Mybatis控制台打印SQL语句的两种方式

    问题描述在使用mybatis进行开发的时候,由于可以动态拼接sql,这样大大方便了我们.但是也有一定的问题,当我们动态sql拼接的块很多的时候,我们要想从*mapper.xml中直接找出完整的sql就 ...

随机推荐

  1. CF1625D.Binary Spiders

    \(\text{Problem}\) 大概就是给出 \(n\) 个数和 \(m\),要从中选最多的数使得两两异或值大于等于 \(m\) 输出方案 \(\text{Solution}\) 一开始的想法很 ...

  2. 深入理解跳表及其在Redis中的应用

    前言 跳表可以达到和红黑树一样的时间复杂度 O(logN),且实现简单,Redis 中的有序集合对象的底层数据结构就使用了跳表.其作者威廉·普评价:跳跃链表是在很多应用中有可能替代平衡树的一种数据结构 ...

  3. 学习ASP.NET Core Blazor编程系列二十七——JWT登录(1)

    学习ASP.NET Core Blazor编程系列文章之目录 学习ASP.NET Core Blazor编程系列一--综述 学习ASP.NET Core Blazor编程系列二--第一个Blazor应 ...

  4. 利用自定义ref实现防抖

    1. debounce.js import { customRef } from 'vue'; export function debounceRef(value, delay = 1000) { l ...

  5. 【1】部署环境python+pycharm+JDK+SDK+node安装+appium安装+Appium-python-client安装及配置+(模拟器)

    安装SDK检查命令  adb doctor 安装JDK,SDK是为了手机端应用程序的访问去做的基础库的搭建 JDK,SDK环境变量需要在一块,不要一个上,一个下 JAVA_HOME  Path  在一 ...

  6. .net core使用 ELK

    一 Linux 下安装部署 第一种方法:docker-compose 安装方式 1.1 创建 docker-compose.yml 文件 version: '3.1' services: elasti ...

  7. windows自带xbox game bar如何更改录制视频保存位置

      若要更改保存游戏剪辑的位置,请使用文件资源管理器根据需要将"捕获"文件夹移动到电脑上的任意位置. Windows 会将游戏剪辑和屏幕截图保存在该文件夹中(无论移动到哪里).   ...

  8. Python语言课程实验报告

    Python语言基础实验 一.实验目的和要求 1.了解Python的基本数据类型: 2.学习了解变量的定义与使用: 3.学会使用Python运算符: 4.掌握数据类型的实际应用. 二.实验环境 软件版 ...

  9. python获取上月、当月、下月的开始和结束日期

    获取上月开始结束日期 方法一 import datetime def get_date_of_last_month(form="%Y-%m-%d"): ""&q ...

  10. 阶梯场景jp@gc - Stepping Thread Group (deprecated)

    1.新建线程,添加配置元件.监听器 由上可见: 需要启动100个线程, 然后间隔30s就持续5s去启动10个线程, 那么就需要这样重复操作10次,才能100个线程全部启动. 最后整体100个线程持续运 ...