两学一做教育纪实评价系统网站,找人做公司网站,做VIP视频网站赚钱,查询网站后台登陆地址文章目录 前言一、基本功能介绍 1.1本章功能效果预览图: 二、可执行源码 2.1 yaml基础配置2.2 MybatisAnalyzeSQLInterceptor实现SQL拦截 前言 SQL性能监控是一个程序必要的功能#xff0c;通常我们可以使用数据库自带的客户端工具进行SQL性能分析。然而对于一些专业度不高…文章目录前言一、基本功能介绍1.1本章功能效果预览图:二、可执行源码2.1 yaml基础配置2.2 MybatisAnalyzeSQLInterceptor实现SQL拦截前言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中以方便后续面向微服务时可以一键开启和关闭这里就不再演示后续扩展可有您自主实现。mybatis-analyze: show-log: true #SQL打印到控制台2.2 MybatisAnalyzeSQLInterceptor实现SQL拦截源码可直接复制运行package com.hl.by.common.mybatis.interceptor; import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.time.StopWatch; import org.apache.ibatis.cache.CacheKey; import org.apache.ibatis.executor.Executor; import org.apache.ibatis.executor.statement.RoutingStatementHandler; 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.mapping.ParameterMode; 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.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import java.sql.Connection; import java.sql.Timestamp; import java.text.SimpleDateFormat; import java.util.*; import java.util.concurrent.TimeUnit; /** * Author: DI.YIN * Date: 2024/11/25 16:32 * Version: 1.0.0 * Description: Mybatis SQL分析插件 **/ Slf4j Intercepts(value { Signature(type StatementHandler.class, method prepare, args {Connection.class, Integer.class}), Signature(type Executor.class, method update, args {MappedStatement.class, Object.class}), 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}), }) Component public class MybatisAnalyzeSQLInterceptor implements Interceptor { Value(${mybatis-analyze.show-log:false}) private Boolean showLog; Override public Object intercept(Invocation invocation) throws Throwable { StopWatch startedWatch StopWatch.createStarted(); Object returnValue null; Exception proceedSQLException null; try { returnValue invocation.proceed(); } catch (Exception e) { proceedSQLException e; } startedWatch.stop(); long spendTime startedWatch.getTime(TimeUnit.MILLISECONDS); if (invocation.getArgs() null || !(invocation.getArgs()[0] instanceof MappedStatement)) { return returnValue; } // just handle mappedStatement MappedStatement mappedStatement (MappedStatement) invocation.getArgs()[0]; // get BoundSql BoundSql boundSql null; for (int i invocation.getArgs().length - 1; i 0; i--) { if (invocation.getArgs()[i] instanceof BoundSql) { boundSql (BoundSql) invocation.getArgs()[i]; break; } } if (invocation.getTarget() instanceof RoutingStatementHandler) { RoutingStatementHandler routingStatementHandler (RoutingStatementHandler) invocation.getTarget(); boundSql routingStatementHandler.getBoundSql(); } if (boundSql null) { Object parameter null; if (invocation.getArgs().length 1) { parameter invocation.getArgs()[1]; } boundSql mappedStatement.getBoundSql(parameter); } // printProcessedSQL(boundSql, mappedStatement.getConfiguration(), mappedStatement.getId(), spendTime); // If an exception occurs during SQL execution,throw exception if (proceedSQLException ! null) { throw proceedSQLException; } return returnValue; } /** * Parse SQL and Print SQL * * param boundSql * param configuration * param statement * param spendTime */ private void printProcessedSQL(BoundSql boundSql, Configuration configuration, String statement, long spendTime) { MapInteger, Object parameterValueMap parseParameterValues(configuration, boundSql); String finalSQL fillSqlParams(boundSql.getSql(), parameterValueMap); finalSQL finalSQL.replaceAll( , ); String printData Start Print SQL Mapper Method: [ statement ] Execute SQL: finalSQL Spend Time: spendTime ms End Print SQL ; if (showLog) { log.info(printData); } } public static String fillSqlParams(String statementQuery, MapInteger, Object parameterValues) { final StringBuilder sb new StringBuilder(); int currentParameter 0; for (int pos 0; pos statementQuery.length(); pos) { char character statementQuery.charAt(pos); if (statementQuery.charAt(pos) ? currentParameter parameterValues.size()) { Object value parameterValues.get(currentParameter); sb.append(value ! null ? value.toString() : new MybatisAnalyzeSQLInterceptor.Values().toString()); currentParameter; } else { sb.append(character); } } return sb.toString(); } /** * 用于解析参数值 * * param configuration * param boundSql * return MapInteger, Object */ private static MapInteger, Object parseParameterValues(Configuration configuration, BoundSql boundSql) { Object parameterObject boundSql.getParameterObject(); ListParameterMapping parameterMappings boundSql.getParameterMappings(); if (parameterMappings ! null) { MapInteger, Object parameterValues new HashMap(); TypeHandlerRegistry typeHandlerRegistry configuration.getTypeHandlerRegistry(); for (int i 0; i parameterMappings.size(); i) { ParameterMapping parameterMapping parameterMappings.get(i); if (parameterMapping.getMode() ! ParameterMode.OUT) { 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); } parameterValues.put(i, new MybatisAnalyzeSQLInterceptor.Values(value)); } } return parameterValues; } return Collections.emptyMap(); } Override public Object plugin(Object target) { return Plugin.wrap(target, this); } Override public void setProperties(Properties properties0) { } Setter Getter public static class Values { public static final String NORM_DATETIME_PATTERN yyyy-MM-dd HH:mm:ss; public static final String databaseDialectDateFormat NORM_DATETIME_PATTERN; public static final String databaseDialectTimestampFormat NORM_DATETIME_PATTERN; private Object value; public Values(Object valueToSet) { this(); this.value valueToSet; } public Values() { } Override public String toString() { return convertToString(this.value); } public String convertToString(Object value) { String result; if (value null) { result NULL; } else { if (value instanceof byte[]) { result new String((byte[]) value); } else if (value instanceof Timestamp) { result new SimpleDateFormat(databaseDialectTimestampFormat).format(value); } else if (value instanceof Date) { result new SimpleDateFormat(databaseDialectDateFormat).format(value); } else if (value instanceof Boolean) { result Boolean.FALSE.equals(value) ? 0 : 1; } else { result value.toString(); } result quoteIfNeeded(result, value); } return result; } private String quoteIfNeeded(String stringValue, Object obj) { if (stringValue null) { return null; } if (Number.class.isAssignableFrom(obj.getClass()) || Boolean.class.isAssignableFrom(obj.getClass())) { return stringValue; } else { return escape(stringValue) ; } } private String escape(String stringValue) { return stringValue.replaceAll(, ); } } }