druid图形化监控 + MyBatis优化器使用

news2024/12/17 10:22:51

文章目录

    • 1.集成druid图形化监控
        • 1.配置application.yml
        • 2.测试访问 http://localhost:项目端口/druid
    • 2.MyBatis优化器(显示完整sql)
        • 1.目录
        • 2.SqlBeautyInterceptor.java:sql拦截器
        • 3.MybatisConfiguration.java:将sql拦截器注入容器
        • 4.测试
        • 5.MyBatis优化器动态加载
          • 1.修改MybatisConfiguration.java的bean注入方式
          • 2.application.yml中配置启用sql优化器

1.集成druid图形化监控

1.配置application.yml
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: 
    password: 
    url: jdbc:mysql://bj--grp-.sql.tencentcdb.com:24169/sun_frame?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useSSL=false
    type: com.alibaba.druid.pool.DruidDataSource # druid连接池
    druid:
      initial-size: 20 # 初始化连接数
      min-idle: 20 # 最小空闲连接数
      max-active: 100 # 最大连接数
      max-wait: 60000 # 获取连接的最大等待时间
      # druid图形化界面
      stat-view-servlet:
        enabled: true # 是否启用
        url-pattern: /druid/* # 访问路径
        login-username: admin
        login-password: 123456
      filter:
        stat:
          enabled: true
          log-slow-sql: true # 是否打印慢日志
          slow-sql-millis: 2000 # 慢日志阈值,2s

2.测试访问 http://localhost:项目端口/druid

CleanShot 2024-07-10 at 15.11.01@2x

CleanShot 2024-07-10 at 15.15.30@2x

2.MyBatis优化器(显示完整sql)

1.目录

CleanShot 2024-07-10 at 15.25.00@2x

2.SqlBeautyInterceptor.java:sql拦截器
package com.sunxiansheng.inteceptor;

import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.defaults.DefaultSqlSession.StrictMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Field;
import java.sql.Statement;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * SQL优化器:显示完整的SQL
 */
@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 SqlBeautyInterceptor implements Interceptor {

    private static final Logger logger = LoggerFactory.getLogger(SqlBeautyInterceptor.class);

    private static final Set<Class<?>> PRIMITIVE_WRAPPER_CLASSES = new HashSet<>(Arrays.asList(
            Byte.class, Short.class, Integer.class, Long.class, Double.class, Float.class, Character.class, Boolean.class));

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
        long startTime = System.currentTimeMillis();
        try {
            return invocation.proceed();
        } finally {
            long endTime = System.currentTimeMillis();
            long sqlCost = endTime - startTime;
            BoundSql boundSql = statementHandler.getBoundSql();
            String sql = boundSql.getSql();
            Object parameterObject = boundSql.getParameterObject();
            List<ParameterMapping> parameterMappingList = boundSql.getParameterMappings();
            sql = formatSql(sql, parameterObject, parameterMappingList);
            sql = beautifySql(sql);
            logger.info("\n========================\nSQL:\n{}\n执行耗时: [{} ms]\n========================", sql, sqlCost);
        }
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {
    }

    private String formatSql(String sql, Object parameterObject, List<ParameterMapping> parameterMappingList) {
        if (sql == null || sql.trim().isEmpty()) {
            return "";
        }
        sql = beautifySql(sql);
        if (parameterObject == null || parameterMappingList == null || parameterMappingList.isEmpty()) {
            return sql;
        }
        String sqlWithoutReplacePlaceholder = sql;
        try {
            if (isStrictMap(parameterObject.getClass())) {
                StrictMap<?> strictMap = (StrictMap<?>) parameterObject;
                if (isList(strictMap.get("list").getClass())) {
                    sql = handleListParameter(sql, (List<?>) strictMap.get("list"));
                }
            } else if (isMap(parameterObject.getClass())) {
                sql = handleMapParameter(sql, (Map<?, ?>) parameterObject, parameterMappingList);
            } else {
                sql = handleCommonParameter(sql, parameterMappingList, parameterObject);
            }
        } catch (Exception e) {
            logger.error("Error formatting SQL: ", e);
            return sqlWithoutReplacePlaceholder;
        }
        return sql;
    }

    private String handleCommonParameter(String sql, List<ParameterMapping> parameterMappingList, Object parameterObject) throws Exception {
        Class<?> parameterObjectClass = parameterObject.getClass();
        List<Field> allFields = new ArrayList<>();
        while (parameterObjectClass != null) {
            allFields.addAll(Arrays.asList(parameterObjectClass.getDeclaredFields()));
            parameterObjectClass = parameterObjectClass.getSuperclass();
        }
        for (ParameterMapping parameterMapping : parameterMappingList) {
            String propertyValue;
            String propertyName = parameterMapping.getProperty();
            Field field = allFields.stream().filter(f -> f.getName().equals(propertyName)).findFirst().orElse(null);
            if (field != null) {
                field.setAccessible(true);
                propertyValue = String.valueOf(field.get(parameterObject));
                if (parameterMapping.getJavaType().isAssignableFrom(String.class)) {
                    propertyValue = "\"" + propertyValue + "\"";
                }
            } else if (isPrimitiveOrPrimitiveWrapper(parameterObject.getClass())) {
                propertyValue = parameterObject.toString();
            } else {
                continue;
            }
            sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(propertyValue));
        }
        return sql;
    }

    private String handleMapParameter(String sql, Map<?, ?> paramMap, List<ParameterMapping> parameterMappingList) {
        for (ParameterMapping parameterMapping : parameterMappingList) {
            Object propertyName = parameterMapping.getProperty();
            Object propertyValue = paramMap.get(propertyName);
            if (propertyValue != null) {
                if (propertyValue.getClass().isAssignableFrom(String.class)) {
                    propertyValue = "\"" + propertyValue + "\"";
                }
                sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(propertyValue.toString()));
            }
        }
        return sql;
    }

    private String handleListParameter(String sql, Collection<?> col) {
        if (col != null && !col.isEmpty()) {
            for (Object obj : col) {
                String value = obj.toString();
                if (obj.getClass().isAssignableFrom(String.class)) {
                    value = "\"" + value + "\"";
                }
                sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(value));
            }
        }
        return sql;
    }

    private String beautifySql(String sql) {
        // 移除多余的空白字符
        sql = sql.replaceAll("[\\s\n]+", " ").trim();

        // 格式化常见的SQL关键字和子句
        sql = sql.replaceAll("(?i)\\b(SELECT|INSERT INTO|UPDATE|DELETE FROM|FROM|WHERE|SET|VALUES|LEFT JOIN|RIGHT JOIN|INNER JOIN|OUTER JOIN|GROUP BY|ORDER BY|HAVING|LIMIT|OFFSET)\\b", "\n$1");

        // 处理 INSERT INTO 和 VALUES 语句以及括号格式
        sql = sql.replaceAll("(?i)\\b(INSERT INTO [^\\(]+\\()", "\n$1\n  ");
        sql = sql.replaceAll("(?i)\\b(VALUES)\\s*\\(", "\n$1\n  (");
        sql = sql.replaceFirst("\\)\\s*VALUES", "\n)\nVALUES");

        // 对 SQL 的各个部分进行缩进处理
        String[] lines = sql.split("\n");
        StringBuilder formattedSql = new StringBuilder();
        int indentLevel = 0;
        for (String line : lines) {
            if (line.matches("(?i)^\\s*(SELECT|INSERT INTO|UPDATE|DELETE FROM|FROM|WHERE|SET|VALUES|LEFT JOIN|RIGHT JOIN|INNER JOIN|OUTER JOIN|GROUP BY|ORDER BY|HAVING|LIMIT|OFFSET)\\b.*")) {
                if (line.matches("(?i)^\\s*(FROM|WHERE|SET|VALUES|LEFT JOIN|RIGHT JOIN|INNER JOIN|OUTER JOIN|GROUP BY|ORDER BY|HAVING|LIMIT|OFFSET)\\b.*")) {
                    indentLevel--;
                }
                formattedSql.append(repeat("  ", indentLevel)).append(line.trim()).append("\n");
                if (line.matches("(?i)^\\s*(SELECT|INSERT INTO|UPDATE|DELETE FROM)\\b.*")) {
                    indentLevel++;
                }
            } else {
                formattedSql.append(repeat("  ", indentLevel)).append(line.trim()).append("\n");
            }
        }

        // 特殊处理闭括号使其位于新行
        formattedSql = new StringBuilder(formattedSql.toString().replace(") VALUES", "\n) VALUES"));
        formattedSql = new StringBuilder(formattedSql.toString().replaceAll("\\),", "\n),"));

        return formattedSql.toString().trim();
    }

    private String formatValues(String sql) {
        Pattern pattern = Pattern.compile("(?i)(VALUES\\s*\\(([^\\)]+)\\))");
        Matcher matcher = pattern.matcher(sql);
        StringBuffer sb = new StringBuffer();
        while (matcher.find()) {
            String group = matcher.group(2);
            String[] values = group.split(",");
            StringBuilder sbGroup = new StringBuilder();
            sbGroup.append("VALUES (\n  ");
            for (String value : values) {
                sbGroup.append(value.trim()).append(", ");
            }
            sbGroup.setLength(sbGroup.length() - 2); // 移除最后一个逗号和空格
            sbGroup.append("\n)");
            matcher.appendReplacement(sb, sbGroup.toString());
        }
        matcher.appendTail(sb);
        return sb.toString();
    }

    private String repeat(String str, int count) {
        StringBuilder result = new StringBuilder();
        for (int i = 0; i < count; i++) {
            result.append(str);
        }
        return result.toString();
    }

    private boolean isPrimitiveOrPrimitiveWrapper(Class<?> clazz) {
        return clazz.isPrimitive() || PRIMITIVE_WRAPPER_CLASSES.contains(clazz);
    }

    private boolean isStrictMap(Class<?> clazz) {
        return StrictMap.class.isAssignableFrom(clazz);
    }

    private boolean isList(Class<?> clazz) {
        return List.class.isAssignableFrom(clazz);
    }

    private boolean isMap(Class<?> clazz) {
        return Map.class.isAssignableFrom(clazz);
    }
}
3.MybatisConfiguration.java:将sql拦截器注入容器
import com.sunxiansheng.inteceptor.SqlBeautyInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * MyBatis配置类:用于将Bean注入容器
 */
@Configuration
public class MybatisConfiguration {
    // 注入Bean容器
    @Bean
    public SqlBeautyInterceptor sqlBeautyInterceptor() {
        return new SqlBeautyInterceptor();
    }
}
4.测试

CleanShot 2024-07-10 at 16.05.31@2x

5.MyBatis优化器动态加载
1.修改MybatisConfiguration.java的bean注入方式
package com.sunxiansheng.config;

import com.sunxiansheng.inteceptor.SqlBeautyInterceptor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * MyBatis配置类:用于将Bean注入容器
 */
@Configuration
public class MybatisConfiguration {
    // 注入Bean容器
    @Bean
    // 只有当 sql.beauty.show 的值为 "true" 时,相关的配置或 bean 才会被注册。
    // matchIfMissing: 如果设置为 true,这意味着如果在配置中没有找到 name 指定的属性,则条件视为匹配。
    @ConditionalOnProperty(name = {"sql.beauty.show"}, havingValue = "true", matchIfMissing = true)
    public SqlBeautyInterceptor sqlBeautyInterceptor() {
        System.out.println();
        return new SqlBeautyInterceptor();
    }
}

CleanShot 2024-07-10 at 16.24.56@2x

2.application.yml中配置启用sql优化器

CleanShot 2024-07-10 at 16.25.22@2x

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2261002.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

1216作业

思维导图 作业 使用无名信号量实现输出春夏秋冬 #include <myhead.h> sem_t sem1,sem2,sem3,sem4; void *fun1() {while(1){sem_wait(&sem1);sleep(1);printf("春\n");sem_post(&sem2);} }void *fun2() {while(1){sem_wait(&sem2);sleep(1);prin…

学习maven(maven 项目模块化,继承,聚合)

前言 本篇博客的核心&#xff1a;理解maven 项目模块化&#xff0c;继承&#xff0c;聚合 的含义 maven 项目模块化 含义 maven项目模块化&#xff1a;使用maven 构建项目&#xff0c;管理项目的方式&#xff0c;我们可以将maven项目根据内在的关系拆分成很多个小项目【模块】…

【Linux】自定义项目-进度条

更多精彩内容..... &#x1f389;❤️播主の主页✨&#x1f618; Stark、-CSDN博客 准备工作&#xff1a;"\r"与"\n"字符 ①&#xff1a;基本含义 在C语言和Linux环境中&#xff0c;\r是回车符&#xff0c;\n是换行符&#xff0c;用于控制文本格式和输出…

OpenLinkSaas 2025年1月开发计划

先来看看OpenLinkSaas的大目标 在OpenLinkSaas的产品目标中&#xff0c;让开发人员更加方便的使用云资源是目标之一。通过各大云厂商的API&#xff0c;来可视化云上基础设施的数据是远远不够的。我们准备在2025年1月份增加方便管理和运营研发场景下服务器的能力。 这部分的功能…

电工电子技术实验:电压比较器及其应用电路

实验目的 1&#xff0e;了解电压比较器与运算放大器的性能区别&#xff1b; 2&#xff0e;掌握电压比较器的结构及特点&#xff1b; 3&#xff0e;掌握电压比较器电压传输特性的测试方法&#xff1b; 4&#xff0e;学习比较器在电路设计中的应用 实验原理 电压比较器是一…

代理 IP 行业现状与未来趋势分析

随着互联网的飞速发展&#xff0c;代理 IP 行业在近年来逐渐兴起并成为网络技术领域中一个备受关注的细分行业。它在数据采集、网络营销、隐私保护等多个方面发挥着重要作用&#xff0c;其行业现状与未来发展趋势值得深入探讨。 目前&#xff0c;代理 IP 行业呈现出以下几个显著…

旅游系统旅游小程序PHP+Uniapp

旅游门票预订系统&#xff0c;支持景点门票、导游产品便捷预订、美食打卡、景点分享、旅游笔记分享等综合系统 更新日志 V1.3.0 1、修复富文本标签 2、新增景点入驻【高级版本】3、新增门票核销【高级版】4、新增门票端口【高级版】

【日常笔记】Spring boot:编写 Content type = ‘text/plain‘ 接口

一、项目场景&#xff1a; 接口&#xff1a;Context-Type&#xff1a;text/plain 方式&#xff1a;POST 项目场景&#xff1a;硬件回调接口 二、实战 PostMapping(value "/xx/xxx", consumes "text/plain" ) 2.1、接口 /*** return String* time 202…

STM32F407+LAN8720A +LWIP +FreeRTOS UDP通讯

STM32F407+LAN8720A +LWIP +FreeRTOS ping通 上一篇实现了LWIP ping 通 本篇实现UDP通讯 实现如下功能: 串口1空闲中断+DMA接收,收到数据用UDP发送UDP接收,收到数据用串口1发送STM32CUBEIDE配置和代码 1. 配置UARAT1的空闲中断+DMA接收 UART1接收到数据,释放信号量,在任…

KeyFormer:使用注意力分数压缩KV缓存

Keyformer: KV Cache Reduction through Key Tokens Selection for Efficient Generative Inference 202403&#xff0c;发表在Mlsys Introduction 优化KV cache的策略&#xff0c;主要是集中在系统级别的优化上&#xff0c;比如FlashAttention、PagedAttention&#xff0c;它…

3.9 网际控制报文协议ICMP

欢迎大家订阅【计算机网络】学习专栏&#xff0c;开启你的计算机网络学习之旅&#xff01; 文章目录 前言1 ICMP报文的封装2 ICMP差错报告报文的类型3 不应发送ICMP差错报告报文的情况4 常用的ICMP询问报文类型5 ICMP的应用 前言 网际控制报文协议&#xff08;ICMP&#xff09…

东北大学《2024年839自动控制原理真题》 (完整版)

本文内容&#xff0c;全部选自自动化考研联盟的&#xff1a;《东北大学839自控考研资料》的真题篇。后续会持续更新更多学校&#xff0c;更多年份的真题&#xff0c;记得关注哦~ 目录 2024年真题 Part1&#xff1a;2024年完整版真题 2024年真题

百度智能云千帆AppBuilder升级,百度AI搜索组件上线,RAG支持无限容量向量存储!

百度智能云千帆 AppBuilder 发版升级&#xff01; 进一步降低开发门槛&#xff0c;落地大模型到应用的最后一公里。在千帆 AppBuilder 最新升级的 V1.1版本中&#xff0c;企业级 RAG 和 Agent 能力再度提升&#xff0c;同时组件生态与应用集成分发更加优化。 • 企业级 RAG&am…

就业相关(硕士)

一、嵌入式 1.机器人行业 1.1 大致情况 要做机器人行业&#xff0c;主要技术栈是运动控制、深度学习、强化学习、具身智能等&#xff0c;主要求职方向有运动控制算法工程师和机器人算法工程师等等。大致薪资在30w到50w不等&#xff0c;主要看方向&#xff08;双211&#xff…

Selenium操作指南

&#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 大家好&#xff0c;今天带大家一起系统的学习下模拟浏览器运行库Selenium&#xff0c;它是一个用于Web自动化测试及爬虫应用的重要工具。 Selenium测试直接运行在…

OpenCV相机标定与3D重建(11)用于在图像上绘制世界坐标系的三条轴函数drawFrameAxes()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 从姿态估计绘制世界/物体坐标系的轴。 cv::drawFrameAxes 是 OpenCV 库中的一个函数&#xff0c;用于在图像上绘制世界坐标系的三条轴&#xff0…

spring学习(XML中定义与配置bean(超详细)。IoC与DI入门spring案例)

目录 一、配置文件(XML)中定义与配置bean。 &#xff08;1&#xff09;bean的基础配置。&#xff08;id、class&#xff09; &#xff08;2&#xff09;bean的别名配置。 1、基本介绍。 2、demo测试。 3、NoSuchBeanDefinitionException&#xff01; &#xff08;3&#xff09;…

Docker容器编排与Docker Compose

1. Docker Compose介绍与基础概念 Docker Compose是一个用于定义和运行多容器Docker应用的工具。通过Compose&#xff0c;用户可以用YAML文件来定义多个容器的服务、网络、存储等配置&#xff0c;并通过一个命令来启动、停止和管理这些容器。它简化了多容器应用的管理&#xf…

鸿蒙项目云捐助第七讲鸿蒙App应用的首页推荐模块布局的实现

鸿蒙项目云捐助第七讲鸿蒙App应用的首页推荐模块布局的实现 最后设置首页的推荐模块&#xff0c;参考模板如下图所示。 一、首页热门推荐模块的实现 对于热门推荐模块&#xff0c;先有上面的小标题栏&#xff0c;这里的标题栏也有一个小图标&#xff0c;首先从“百度图库”中…

MySQL八股-MVCC入门

文章目录 当前读&#xff08;加锁&#xff09;快照读&#xff08;不加锁&#xff09;MVCC隐藏字段undo-log版本链A. 第一步B.第二步C. 第三步 readview MVCC原理分析RCA. 先来看第一次快照读具体的读取过程&#xff1a;B. 再来看第二次快照读具体的读取过程: RR隔离级别 当前读…