用mybatis插件抓取最终sql
痛点概述当我们在排查bug ,需要看执行的完整sql 时,在 console可以拿到如图的sql然后手工一个一个的替换问号占位符后,去MySQL 执行,看sql有木有什么问题。如果sql简单,那比较好说, 如果是个复杂sql,手动替换N个问号占位符,这种痛相信大家都经历过。 今天介绍的 MybatisFinalSqlPlugin 插件 正是解决了这样的痛点,可以直接抓到最终sql。环境mybati
·
痛点概述
当我们在排查bug ,需要看执行的完整sql 时,在 console可以拿到如图的sql 
然后手工一个一个的替换问号占位符后,去MySQL 执行,看sql有木有什么问题。如果sql简单,那比较好说, 如果是个复杂sql,手动替换N个问号占位符,这种痛相信大家都经历过。 今天介绍的 MybatisFinalSqlPlugin 插件 正是解决了这样的痛点,可以直接抓到最终sql。
环境
- mybatis 3.4.2
实现思路
最近在走读mybatis 核心源码时,发现mybatis自己有构建最终sql,就想着把代码抠出来用,后面找到一个切入点,就是通过mybatis 插件机制,在插件里面获取 mybatis 的几大核心对象,然后获取到 mybatis 自己构建的最终sql,大功告成,详细代码如下。
源码所在项目
MybatisFinalSqlPlugin 插件 完整代码
package com.anuo.app.common.datalayer.mybatisplugin;
import com.google.common.base.Stopwatch;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.Statement;
import java.util.Properties;
@Intercepts({ @Signature(type = StatementHandler.class, method = "prepare", args = { Connection.class,Integer.class }) })
@Slf4j
public class MybatisFinalSqlPlugin implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
Stopwatch stopwatch = Stopwatch.createStarted();
if (invocation.getTarget() instanceof StatementHandler) {
//获取Statement 对象
Connection conn = (Connection) invocation.getArgs()[0];
StatementHandler handler = (StatementHandler) invocation.getTarget();
Statement stmt = handler.prepare(conn, 30);
handler.parameterize(stmt);
//获取真实对象
MetaObject metaObject = SystemMetaObject.forObject(stmt);
while (metaObject.hasGetter("h")) {
Object obj = metaObject.getValue("h");
// 将对象包装成MetaObject对象后就成了真实对象,然后就可以通过反射技术可以操作真实对象的所有属性
metaObject = SystemMetaObject.forObject(obj);
}
//通过反射获取 Statement 对象上的FinalSql
//todo:后续用 metaObject.getValue() 重构下面的反射代码,提升性能
Field hField = metaObject.getClass().getDeclaredField("originalObject");
hField.setAccessible(true);
Object hObj = hField.get(metaObject);
Field statementField = hObj.getClass().getDeclaredField("statement");
statementField.setAccessible(true);
Object statementObj = statementField.get(hObj);
Field stmtField = statementObj.getClass().getDeclaredField("stmt");
stmtField.setAccessible(true);
Object stmtObj = stmtField.get(statementObj);
Field statementArrivedField = stmtObj.getClass().getDeclaredField("statement");
statementArrivedField.setAccessible(true);
Object statementArrivedFieldObj = statementArrivedField.get(stmtObj);
String finalSql = statementArrivedFieldObj.toString();
//去掉不要的字符串
finalSql = finalSql.substring(finalSql.lastIndexOf(":") + 1, finalSql.length() - 1);
log.info("最终sql: \n " + finalSql);
}
//做了下性能测试 平均耗时为 1,2毫秒,非常低,不错!
log.debug("抓取最终sql 耗时:" + stopwatch);
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
if (target instanceof StatementHandler) {
return Plugin.wrap(target, this);
} else {
return target;
}
}
@Override
public void setProperties(Properties properties) {
}
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐



所有评论(0)