Mybatis控制台打印SQL实行信息(实行方法、实行SQL、实行时间) ...

打印 上一主题 下一主题

主题 1607|帖子 1607|积分 4821


前言

SQL性能监控是一个步伐必要的功能,通常我们可以利用数据库自带的客户端工具进行SQL性能分析。然而对于一些专业度不高的人员来说,当步伐出现卡顿或者相应速度变慢时,排查标题变得困难。当步伐出现卡顿,通常通过检查服务器磁盘利用情况、步伐内存巨细,网络带宽以及数据库I/O等方面进行标题排查。然而数据库I/O打高的情况通常是由于SQL实行服从过低导致的。一般项目制的公司都有属于本身的实施人员,然而要让实施人员去排查具体SQL实行过慢标题,这显然对于专业度不高的工作人员来说是一种挑战和煎熬。因此本系列文章将介绍怎样利用Mybatis的拦截器功能完成对SQL实行的时间记载,并通过MQ推送至SQL记载服务,记载具体的慢SQL信息,后续可以通过页面进行展示。通过可视化的方式让实施人员快速定位到标题所在。

一、根本功能介绍

本章节只实现Mybatis实行时对实行SQL进行拦截,控制台打印实行SQL包罗参数、实行方法以及实行时间。大抵结构图如下:

对慢SQL进行发送MQ,记载表现到前端界面的功能,将在本系列文章第二章实现。
1.1本章功能效果预览图:


   Mapper Method: 表现该SQL是由哪个Mapper方法进行调用实行。
Execute SQL:打印出完整实行的SQL,自动添补了参数。
Spend Time:记载本次SQL实行耗费的时间。
  二、可实行源码

2.1 yaml底子设置

必要在yaml设置文件中设置是否打印SQL实行信息。当然该设置可以放入Redis中,以方便后续面向微服务时,可以一键开启和关闭,这里就不再演示,后续扩展可有您自主实现。
  1. mybatis-analyze:
  2.   show-log: true #SQL打印到控制台
复制代码

2.2 MybatisAnalyzeSQLInterceptor实现SQL拦截

源码可直接复制运行!!!!!
  1. package com.hl.by.common.mybatis.interceptor;
  2. import lombok.Getter;
  3. import lombok.Setter;
  4. import lombok.extern.slf4j.Slf4j;
  5. import org.apache.commons.lang3.time.StopWatch;
  6. import org.apache.ibatis.cache.CacheKey;
  7. import org.apache.ibatis.executor.Executor;
  8. import org.apache.ibatis.executor.statement.RoutingStatementHandler;
  9. import org.apache.ibatis.executor.statement.StatementHandler;
  10. import org.apache.ibatis.mapping.BoundSql;
  11. import org.apache.ibatis.mapping.MappedStatement;
  12. import org.apache.ibatis.mapping.ParameterMapping;
  13. import org.apache.ibatis.mapping.ParameterMode;
  14. import org.apache.ibatis.plugin.*;
  15. import org.apache.ibatis.reflection.MetaObject;
  16. import org.apache.ibatis.session.Configuration;
  17. import org.apache.ibatis.session.ResultHandler;
  18. import org.apache.ibatis.session.RowBounds;
  19. import org.apache.ibatis.type.TypeHandlerRegistry;
  20. import org.springframework.beans.factory.annotation.Value;
  21. import org.springframework.stereotype.Component;
  22. import java.sql.Connection;
  23. import java.sql.Timestamp;
  24. import java.text.SimpleDateFormat;
  25. import java.util.*;
  26. import java.util.concurrent.TimeUnit;
  27. /**
  28. * @Author: DI.YIN
  29. * @Date: 2024/11/25 16:32
  30. * @Version: 1.0.0
  31. * @Description: Mybatis SQL分析插件
  32. **/
  33. @Slf4j
  34. @Intercepts(value = {
  35.         @Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class}),
  36.         @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
  37.         @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
  38.         @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}),
  39. })
  40. @Component
  41. public class MybatisAnalyzeSQLInterceptor implements Interceptor {
  42.     @Value("${mybatis-analyze.show-log:false}")
  43.     private Boolean showLog;
  44.     @Override
  45.     public Object intercept(Invocation invocation) throws Throwable {
  46.         StopWatch startedWatch = StopWatch.createStarted();
  47.         Object returnValue = null;
  48.         Exception proceedSQLException = null;
  49.         try {
  50.             returnValue = invocation.proceed();
  51.         } catch (Exception e) {
  52.             proceedSQLException = e;
  53.         }
  54.         startedWatch.stop();
  55.         long spendTime = startedWatch.getTime(TimeUnit.MILLISECONDS);
  56.         if (invocation.getArgs() == null || !(invocation.getArgs()[0] instanceof MappedStatement)) {
  57.             return returnValue;
  58.         }
  59.         // just handle mappedStatement
  60.         MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
  61.         // get BoundSql
  62.         BoundSql boundSql = null;
  63.         for (int i = invocation.getArgs().length - 1; i >= 0; i--) {
  64.             if (invocation.getArgs()[i] instanceof BoundSql) {
  65.                 boundSql = (BoundSql) invocation.getArgs()[i];
  66.                 break;
  67.             }
  68.         }
  69.         if (invocation.getTarget() instanceof RoutingStatementHandler) {
  70.             RoutingStatementHandler routingStatementHandler = (RoutingStatementHandler) invocation.getTarget();
  71.             boundSql = routingStatementHandler.getBoundSql();
  72.         }
  73.         if (boundSql == null) {
  74.             Object parameter = null;
  75.             if (invocation.getArgs().length > 1) {
  76.                 parameter = invocation.getArgs()[1];
  77.             }
  78.             boundSql = mappedStatement.getBoundSql(parameter);
  79.         }
  80.         //
  81.         printProcessedSQL(boundSql, mappedStatement.getConfiguration(), mappedStatement.getId(), spendTime);
  82.         // If an exception occurs during SQL execution,throw exception
  83.         if (proceedSQLException != null) {
  84.             throw proceedSQLException;
  85.         }
  86.         return returnValue;
  87.     }
  88.     /**
  89.      * Parse SQL and Print SQL
  90.      *
  91.      * @param boundSql
  92.      * @param configuration
  93.      * @param statement
  94.      * @param spendTime
  95.      */
  96.     private void printProcessedSQL(BoundSql boundSql, Configuration configuration, String statement, long spendTime) {
  97.         Map<Integer, Object> parameterValueMap = parseParameterValues(configuration, boundSql);
  98.         String finalSQL = fillSqlParams(boundSql.getSql(), parameterValueMap);
  99.         finalSQL = finalSQL.replaceAll("\n", "");
  100.         String printData = "\n===============Start Print SQL===============\n" +
  101.                 "Mapper Method: [ " + statement + " ]\n" +
  102.                 "Execute SQL: " + finalSQL + " \n" +
  103.                 "Spend Time: " + spendTime + " ms \n" +
  104.                 "===============End Print SQL===============\n";
  105.         if (showLog) {
  106.             log.info(printData);
  107.         }
  108.     }
  109.     public static String fillSqlParams(String statementQuery, Map<Integer, Object> parameterValues) {
  110.         final StringBuilder sb = new StringBuilder();
  111.         int currentParameter = 0;
  112.         for (int pos = 0; pos < statementQuery.length(); pos++) {
  113.             char character = statementQuery.charAt(pos);
  114.             if (statementQuery.charAt(pos) == '?' && currentParameter <= parameterValues.size()) {
  115.                 Object value = parameterValues.get(currentParameter);
  116.                 sb.append(value != null ? value.toString() : new MybatisAnalyzeSQLInterceptor.Values().toString());
  117.                 currentParameter++;
  118.             } else {
  119.                 sb.append(character);
  120.             }
  121.         }
  122.         return sb.toString();
  123.     }
  124.     /**
  125.      * 用于解析参数值
  126.      *
  127.      * @param configuration
  128.      * @param boundSql
  129.      * @return Map<Integer, Object>
  130.      */
  131.     private static Map<Integer, Object> parseParameterValues(Configuration configuration, BoundSql boundSql) {
  132.         Object parameterObject = boundSql.getParameterObject();
  133.         List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
  134.         if (parameterMappings != null) {
  135.             Map<Integer, Object> parameterValues = new HashMap<>();
  136.             TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
  137.             for (int i = 0; i < parameterMappings.size(); i++) {
  138.                 ParameterMapping parameterMapping = parameterMappings.get(i);
  139.                 if (parameterMapping.getMode() != ParameterMode.OUT) {
  140.                     Object value;
  141.                     String propertyName = parameterMapping.getProperty();
  142.                     if (boundSql.hasAdditionalParameter(propertyName)) {
  143.                         value = boundSql.getAdditionalParameter(propertyName);
  144.                     } else if (parameterObject == null) {
  145.                         value = null;
  146.                     } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
  147.                         value = parameterObject;
  148.                     } else {
  149.                         MetaObject metaObject = configuration.newMetaObject(parameterObject);
  150.                         value = metaObject.getValue(propertyName);
  151.                     }
  152.                     parameterValues.put(i, new MybatisAnalyzeSQLInterceptor.Values(value));
  153.                 }
  154.             }
  155.             return parameterValues;
  156.         }
  157.         return Collections.emptyMap();
  158.     }
  159.     @Override
  160.     public Object plugin(Object target) {
  161.         return Plugin.wrap(target, this);
  162.     }
  163.     @Override
  164.     public void setProperties(Properties properties0) {
  165.     }
  166.     @Setter
  167.     @Getter
  168.     public static class Values {
  169.         public static final String NORM_DATETIME_PATTERN = "yyyy-MM-dd HH:mm:ss";
  170.         public static final String databaseDialectDateFormat = NORM_DATETIME_PATTERN;
  171.         public static final String databaseDialectTimestampFormat = NORM_DATETIME_PATTERN;
  172.         private Object value;
  173.         public Values(Object valueToSet) {
  174.             this();
  175.             this.value = valueToSet;
  176.         }
  177.         public Values() {
  178.         }
  179.         @Override
  180.         public String toString() {
  181.             return convertToString(this.value);
  182.         }
  183.         public String convertToString(Object value) {
  184.             String result;
  185.             if (value == null) {
  186.                 result = "NULL";
  187.             } else {
  188.                 if (value instanceof byte[]) {
  189.                     result = new String((byte[]) value);
  190.                 } else if (value instanceof Timestamp) {
  191.                     result = new SimpleDateFormat(databaseDialectTimestampFormat).format(value);
  192.                 } else if (value instanceof Date) {
  193.                     result = new SimpleDateFormat(databaseDialectDateFormat).format(value);
  194.                 } else if (value instanceof Boolean) {
  195.                     result = Boolean.FALSE.equals(value) ? "0" : "1";
  196.                 } else {
  197.                     result = value.toString();
  198.                 }
  199.                 result = quoteIfNeeded(result, value);
  200.             }
  201.             return result;
  202.         }
  203.         private String quoteIfNeeded(String stringValue, Object obj) {
  204.             if (stringValue == null) {
  205.                 return null;
  206.             }
  207.             if (Number.class.isAssignableFrom(obj.getClass()) || Boolean.class.isAssignableFrom(obj.getClass())) {
  208.                 return stringValue;
  209.             } else {
  210.                 return "'" + escape(stringValue) + "'";
  211.             }
  212.         }
  213.         private String escape(String stringValue) {
  214.             return stringValue.replaceAll("'", "''");
  215.         }
  216.     }
  217. }
复制代码


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

小秦哥

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表