Эх сурвалжийг харах

修改mybatis拦截器实现,获取真实SQL内容并格式化

Signed-off-by: luoshi <luoshi@qmth.com.cn>
luoshi 11 сар өмнө
parent
commit
fad1edec57

+ 83 - 13
data-mybatis-plus/src/main/java/com/qmth/boot/mybatis/plugin/MybatisProfilerPlugin.java

@@ -3,23 +3,31 @@ package com.qmth.boot.mybatis.plugin;
 import com.qmth.boot.mybatis.config.ProfilerProperties;
 import org.apache.ibatis.cache.CacheKey;
 import org.apache.ibatis.executor.Executor;
-import org.apache.ibatis.executor.statement.StatementHandler;
 import org.apache.ibatis.mapping.BoundSql;
 import org.apache.ibatis.mapping.MappedStatement;
+import org.apache.ibatis.mapping.ParameterMapping;
 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 org.springframework.util.ObjectUtils;
 
+import java.text.DateFormat;
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
 import java.util.Properties;
+import java.util.regex.Matcher;
 
-@Intercepts(value = {
-        @Signature(type = Executor.class, method = "update", args = { MappedStatement.class, Object.class }),
+@Intercepts(value = { @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 = "query", args = { MappedStatement.class, Object.class,
-                RowBounds.class, ResultHandler.class }) })
+        @Signature(type = Executor.class, method = "update", args = { MappedStatement.class, Object.class }) })
 public class MybatisProfilerPlugin implements Interceptor {
 
     private static final Logger log = LoggerFactory.getLogger(MybatisProfilerPlugin.class);
@@ -34,16 +42,78 @@ public class MybatisProfilerPlugin implements Interceptor {
     @Override
     public Object intercept(Invocation invocation) throws Throwable {
         long start = System.currentTimeMillis();
-        try {
-            return invocation.proceed();
-        } finally {
-            long cost = System.currentTimeMillis() - start;
-            if (profilerProperties.isEnable() && cost > profilerProperties.getThreshold().toMillis()) {
-                StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
-                String sql = statementHandler.getBoundSql().getSql();
-                log.info("Sql time cost: " + cost + "ms\n" + sql);
+        Object result = invocation.proceed();
+        long cost = System.currentTimeMillis() - start;
+        if (profilerProperties.isEnable() && cost > profilerProperties.getThreshold().toMillis()) {
+            String sql = generateSql(invocation);
+            log.info("Sql time cost: " + cost + "ms\n" + sql);
+        }
+        return result;
+    }
+
+    private static String generateSql(Invocation invocation) {
+        // 获取到BoundSql以及Configuration对象
+        // BoundSql 对象存储了一条具体的 SQL 语句及其相关参数信息。
+        // Configuration 对象保存了 MyBatis 框架运行时所有的配置信息
+        MappedStatement statement = (MappedStatement) invocation.getArgs()[0];
+        Object parameter = null;
+        if (invocation.getArgs().length > 1) {
+            parameter = invocation.getArgs()[1];
+        }
+        Configuration configuration = statement.getConfiguration();
+        BoundSql boundSql = statement.getBoundSql(parameter);
+
+        // 获取参数对象
+        Object parameterObject = boundSql.getParameterObject();
+        // 获取参数映射
+        List<ParameterMapping> params = boundSql.getParameterMappings();
+        // 获取到执行的SQL
+        String sql = boundSql.getSql();
+        // SQL中多个空格使用一个空格代替
+        sql = sql.replaceAll("[\\s]+", " ");
+        if (!ObjectUtils.isEmpty(params) && !ObjectUtils.isEmpty(parameterObject)) {
+            // TypeHandlerRegistry 是 MyBatis 用来管理 TypeHandler 的注册器。TypeHandler 用于在 Java 类型和 JDBC 类型之间进行转换
+            TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
+            // 如果参数对象的类型有对应的 TypeHandler,则使用 TypeHandler 进行处理
+            if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
+                sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(getParameterValue(parameterObject)));
+            } else {
+                // 否则,逐个处理参数映射
+                for (ParameterMapping param : params) {
+                    // 获取参数的属性名
+                    String propertyName = param.getProperty();
+                    MetaObject metaObject = configuration.newMetaObject(parameterObject);
+                    // 检查对象中是否存在该属性的 getter 方法,如果存在就取出来进行替换
+                    if (metaObject.hasGetter(propertyName)) {
+                        Object obj = metaObject.getValue(propertyName);
+                        sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(getParameterValue(obj)));
+                        // 检查 BoundSql 对象中是否存在附加参数。附加参数可能是在动态 SQL 处理中生成的,有的话就进行替换
+                    } else if (boundSql.hasAdditionalParameter(propertyName)) {
+                        Object obj = boundSql.getAdditionalParameter(propertyName);
+                        sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(getParameterValue(obj)));
+                    } else {
+                        // 如果都没有,说明SQL匹配不上,带上“缺失”方便找问题
+                        sql = sql.replaceFirst("\\?", "缺失");
+                    }
+                }
             }
         }
+        return sql;
+    }
+
+    private static String getParameterValue(Object object) {
+        String value = "";
+        if (object instanceof String) {
+            value = "'" + object.toString() + "'";
+        } else if (object instanceof Date) {
+            DateFormat format = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.CHINA);
+            value = "'" + format.format((Date) object) + "'";
+        } else if (object instanceof Boolean) {
+            value = ((Boolean) object) ? "1" : "0";
+        } else if (object != null) {
+            value = object.toString();
+        }
+        return value;
     }
 
     @Override