MyBatis(Plus) 打印SQL, 分析执行时间
MyBatis/MyBatis Plus打印的SQL调试起来比较麻烦
当然IDEA/eclipse都有类似mybatis log plugin这种插件来解析, 但是安装插件有些许弊端, 就写了个工具类处理
如果需要更详细的SQL分析, 建议使用p6spy. 本配置仅仅是为了调试SQL方便
效果展示:
代码:
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, 分析执行时间的更多相关文章
- MyBatis 插件 : 打印 SQL 及其执行时间
Plugins 摘一段来自MyBatis官方文档的文字. MyBatis允许你在某一点拦截已映射语句执行的调用.默认情况下,MyBatis允许使用插件来拦截方法调用: Executor(update. ...
- mybatis日志,打印sql语句,输出sql
mybatis日志,打印sql语句,输出sql<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE ...
- mybatis配置打印sql
mybatis配置打印sql: <settings> <setting name="logImpl" value="STDOUT_LOGGING&quo ...
- Springboot中mybatis控制台打印sql语句
Springboot中mybatis控制台打印sql语句 https://www.jianshu.com/p/3cfe5f6e9174 https://www.jianshu.com/go-wild? ...
- Springboot自定义starter打印sql及其执行时间
前面写到了通过实现mybatis提供的org.apache.ibatis.plugin.Interceptor接口实现了打印SQL执行时间,并格式化SQL及其参数,如果我们使用的是ssm还得再配置文件 ...
- mybatis 控制台打印sql
开发时调试使用 <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBe ...
- mybatis 控制台打印sql语句
其实很简单,打印SQL只需要加一个setting就可以了.亲测可用. mybatis-config.xml: <settings> <setting name=&quo ...
- spring-mvc Mybatis插件打印SQL
代码: package com.chainup.exchange.service.adapter; import com.chainup.exchange.service.impl.AccountSe ...
- mybatis logback打印sql
<?xml version="1.0" encoding="UTF-8" ?><configuration> <contextNa ...
- Mybatis控制台打印SQL语句的两种方式
问题描述在使用mybatis进行开发的时候,由于可以动态拼接sql,这样大大方便了我们.但是也有一定的问题,当我们动态sql拼接的块很多的时候,我们要想从*mapper.xml中直接找出完整的sql就 ...
随机推荐
- JZOJ 5351. 【NOIP2017提高A组模拟9.7】简单无向图
题目大意 给定 \(n\) 个度数为 \(\in [1,2]\) 之间的点,求能组成多少种简单无向图(可不连通,点与点之间有别) 分析 显然答案只与 \(n1,n2\) 有关 那么 \(dp\)?(我 ...
- 嵌入式Linux—输入子系统
输入系统 常见的输入设备有键盘.鼠标.遥控杆.书写板.触摸屏等等,用户通过这些输入设备与Linux系统进行数据交换. 内核中怎样表示一个输入设备 // include/linux/input.h st ...
- JAVA快速获取网络图片或者URL图片并保存到本地
JAVA快速获取网络图片或者URL图片并保存到本地,直接上代码: package com.xh.service;import org.springframework.stereotype.Servic ...
- Camera | 5.Linux v4l2架构(基于rk3568)
上一篇我们讲解了如何编写基于V4L2的应用程序编写,本文主要讲解内核中V4L2架构,以及一些最重要的结构体.注册函数. 厂家在实现自己的摄像头控制器驱动时,总体上都遵循这个架构来实现,但是不同厂家.不 ...
- 05for循环
for循环 循环的作用与分类 作用:让代码更加高效的重复运行 分类:for循环和while循环 for循环结构 for 临时变量 in 可迭代对象: 重复执行的代码1 重复执行的代码2 ... 可迭代 ...
- sql查询多个结果字段通过逗号分隔为同一行显示、sql查询结果有符号分隔的字段拆分多行显示
一.sql查询多个结果通过逗号分隔为同一行显示 sql查询数据结果 select e.ctrl_desc from t_ctrl_entry e inner join CodeGroupKeyCode ...
- 记一次hooks陷阱
今天写一个hook,正想发挥hooks这种高级复用方式来缩短我的开发时间,就出现了一个新bug. 我编写的这个hook用于管理数据列表状态.除了导出内部的状态外,还导出一些方法供外部调用.代码简化如下 ...
- Vue项目安装less和less-loader
第一步: 查看webpack和webpack-cli是否安装打开cmd,通过命令查看 webpack -v webpack-cli -v 如果没有安装,要先进行安装 可以通过 npm view web ...
- UE打LOG整理
Kismet库 蓝图方法cpp使用 例:打LOG:Print String 蓝图节点的鼠标tips:Target is Kismet System Library #include "Run ...
- python怎么实现正确的浮点数四舍五入
round 以下示例展示对于结构相同的两组数据(1.03575000和1.03425000)保留小数后4位,使用内置函数round方法的输出结果,并不是需要的结果 print(round(1.0357 ...