SpringBoot中扩展Druid的过滤器实现完整的SQL打印

文章目录

  • 前言
  • 正文
    • 环境说明
    • 过滤器扩展
    • 配置数据源和过滤器
    • 数据库配置信息
    • 打印结果

前言

之前通过Mybatis 、Mybatis Plus 的拦截器扩展,实现自定义的Handler,拼接了完整的SQL。
本次使用 Druid 的过滤器来实现这一功能。输出一个完整的sql,并且给出执行的时间。

对Mybatis Plus 拦截器感兴趣的朋友可以移步:https://blog.csdn.net/FBB360JAVA/article/details/132513180

正文

环境说明

基于 Druid 的过滤器,必须先引入 数据库驱动,Druid的依赖。如果你使用的是Mybatis Plus 也需要引入对应的包。

本文基于SpringBoot 3版本,Java 17 !!!

<!--mysql驱动--><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><version>8.3.0</version><exclusions><exclusion><groupId>com.google.protobuf</groupId><artifactId>protobuf-java</artifactId></exclusion></exclusions></dependency>
<!-- 阿里druid依赖 --><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.2.22</version></dependency><!-- mybatis-plus整合 --><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.6</version><!-- 处理依赖错误,mybatis-spring版本太低 Invalid value type for attribute 'factoryBeanObjectType': java.lang.String--><exclusions><exclusion><groupId>org.mybatis</groupId><artifactId>mybatis-spring</artifactId></exclusion></exclusions></dependency><dependency><groupId>org.mybatis</groupId><artifactId>mybatis-spring</artifactId><version>3.0.3</version></dependency>

过滤器扩展

package com.pine.common.database.filter;import cn.hutool.core.text.StrPool;
import com.alibaba.druid.DbType;
import com.alibaba.druid.proxy.jdbc.*;
import com.alibaba.druid.sql.SQLUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.ObjectUtils;import java.util.ArrayList;
import java.util.List;/*** 继承自Slf4jLogFilter的Druid SQL日志过滤器类* 用于自定义SQL日志的输出格式和行为,基于阿里巴巴的Druid数据库连接池* 主要目的是增强日志输出的灵活性和可读性,以及可能的性能优化** @author pine manage* @since 2024-11-01*/
public class DruildSqlSlf4jLogFilter extends com.alibaba.druid.filter.logging.Slf4jLogFilter {private final static Logger logger = LoggerFactory.getLogger(DruildSqlSlf4jLogFilter.class);private static final String PREPARED_STATEMENT_PREFIX = "pstmt-";private final static String CALLABLE_STATEMENT_PREFIX = "cstmt-";private final static String STATEMENT_PREFIX = "stmt-";private final static String CONNECTION_PREFIX = "conn-";public DruildSqlSlf4jLogFilter() {super();setStatementSqlFormatOption(new SQLUtils.FormatOption(false, false));}/*** 当Statement执行出错后调用此方法** @param statement 执行SQL的Statement代理对象* @param sql       执行的SQL语句* @param error     执行过程中捕获的异常*/@SuppressWarnings("PMD")@Overrideprotected void statement_executeErrorAfter(StatementProxy statement, String sql, Throwable error) {if (!isStatementLogErrorEnabled()) {return;}String formattedSql = getFormattedSql(statement, sql);logger.error("[({}{}, {}) executed error.] SQL:{}", CONNECTION_PREFIX, statement.getConnectionProxy().getId(), stmtId(statement), removeBreakingWhitespace(formattedSql), error);}@Overrideprotected void statementPrepareAfter(PreparedStatementProxy statement) {if (isStatementPrepareAfterLogEnabled() && isStatementLogEnabled()) {statementLog("{conn-" + statement.getConnectionProxy().getId() + ", pstmt-" + statement.getId() + "} created. " + removeBreakingWhitespace(statement.getSql()));}}@Overrideprotected void statementCreateAfter(StatementProxy statement) {if (isStatementCreateAfterLogEnabled() && isStatementLogEnabled()) {statementLog("{conn-" + statement.getConnectionProxy().getId() + ", stmt-" + statement.getId() + "} created");}}@Overrideprotected void statementPrepareCallAfter(CallableStatementProxy statement) {if (isStatementPrepareCallAfterLogEnabled() && isStatementLogEnabled()) {statementLog("{conn-" + statement.getConnectionProxy().getId() + ", cstmt-" + statement.getId() + "} created. " + statement.getSql());}}@Overrideprotected void statementExecuteAfter(StatementProxy statement, String sql, boolean firstResult) {logExecutableSql(statement, sql);}@Overrideprotected void statementExecuteBatchAfter(StatementProxy statement, int[] result) {String sql;if (statement instanceof PreparedStatementProxy) {sql = ((PreparedStatementProxy) statement).getSql();} else {sql = statement.getBatchSql();}logExecutableSql(statement, sql);}@Overrideprotected void statementExecuteQueryAfter(StatementProxy statement, String sql, ResultSetProxy resultSet) {logExecutableSql(statement, sql);}@Overrideprotected void statementExecuteUpdateAfter(StatementProxy statement, String sql, int updateCount) {logExecutableSql(statement, sql);}private String buildMessage(String connectionId, String statementPrefix, String statementId, String sql) {return "{" + CONNECTION_PREFIX + connectionId + ", " + statementPrefix + statementId + "} created. SQL:" + sql;}private void logExecutableSql(StatementProxy statement, String sql) {statement.setLastExecuteTimeNano();double nanos = statement.getLastExecuteTimeNano();double millis = nanos / (1000 * 1000);String formattedSql = getFormattedSql(statement, sql);logger.info("[({}{}, {}) executed. cost {} millis.] SQL:{}  ", CONNECTION_PREFIX, statement.getConnectionProxy().getId(), stmtId(statement), millis, removeBreakingWhitespace(formattedSql));}private String stmtId(StatementProxy statement) {StringBuilder buf = new StringBuilder();if (statement instanceof CallableStatementProxy) {buf.append(CALLABLE_STATEMENT_PREFIX);} else if (statement instanceof PreparedStatementProxy) {buf.append(PREPARED_STATEMENT_PREFIX);} else {buf.append(STATEMENT_PREFIX);}buf.append(statement.getId());return buf.toString();}/*** 格式化SQL语句* 此方法旨在将给定的SQL语句进行格式化,以便在日志输出或者调试时更加清晰易读* 它通过移除多余的空格和换行符,同时在关键字和操作符周围保持适当的空格,以达到格式化的目的** @param statement 代理声明对象,用于执行SQL语句的对象,此处未使用,但可能在将来或特定情况下需要* @param sql       待格式化处理的原始SQL字符串* @return 格式化后的SQL字符串*/private String getFormattedSql(StatementProxy statement, String sql) {int parametersSize = statement.getParametersSize();// 当前sql无参数if (parametersSize == 0) {return sql;}List<Object> parameters = new ArrayList<>(parametersSize);for (int i = 0; i < parametersSize; ++i) {JdbcParameter jdbcParam = statement.getParameter(i);parameters.add(jdbcParam != null ? jdbcParam.getValue() : null);}String dbType = statement.getConnectionProxy().getDirectDataSource().getDbType();return SQLUtils.format(sql, DbType.of(dbType), parameters, this.getStatementSqlFormatOption());}/*** 将所有空白符号替换成空格** @param original 原始字符串* @return 转换之后的字符串*/protected String removeBreakingWhitespace(String original) {if (ObjectUtils.isEmpty(original)) {return original;}StringBuilder builder = new StringBuilder(original.length());for (char c : original.toCharArray()) {if (!Character.isWhitespace(c)) {builder.append(c);} else {builder.append(StrPool.C_SPACE);}}return builder.toString();}}

配置数据源和过滤器

在你自己的配置类中进行定义。

@Beanpublic DruildSqlSlf4jLogFilter slf4jLogFilter() {return new DruildSqlSlf4jLogFilter();}@Bean@Primary@ConfigurationProperties("spring.datasource.druid")public DataSource masterDataSource(@Autowired DataSourceProperties dataSourceProperties) {DruidDataSource dataSource = new DruidDataSource();dataSource.setUrl(dataSourceProperties.getUrl());dataSource.setUsername(dataSourceProperties.getUsername());dataSource.setPassword(dataSourceProperties.getPassword());dataSource.setDriverClassName(dataSourceProperties.getDriverClassName());List<Filter> proxyFilters = dataSource.getProxyFilters();proxyFilters.add(slf4jLogFilter());return dataSource;}

数据库配置信息

在你的配置文件中配置如下内容

# 数据库配置
spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/pine_manage?useUnicode=true&serverTimezone=UTC&useServerPrepStmts=true&rewriteBatchedStatements=trueusername: rootpassword: root123456type: com.alibaba.druid.pool.DruidDataSource

打印结果

可以看到连接器ID,statement ID,执行耗时(单位:毫秒),以及填充了参数的sql语句。

 [(conn-10001, pstmt-20002) executed. cost 14.564691 millis.] SQL:select id, name, code, status, remark , create_time, update_time, deleted from sys_dict where deleted = 0 and code = 'desensitized_field_value_in_json'  [(conn-10001, pstmt-20001) executed. cost 11.126154 millis.] SQL:select id, dict_id, name, value, status , sort, remark, create_time, update_time, deleted from sys_dict_item where deleted = 0 and dict_id = 2 order by id desc, sort asc  

有兴趣的朋友可以调整为按配置生效,配置这个sql过滤器是否注入。更加灵活些。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

企业如何通过架构蓝图实现数字化转型

数字化转型的关键——架构蓝图的力量 在当今的商业世界&#xff0c;数字化转型已经不再是一个选择&#xff0c;而是企业生存与发展不可回避的战略行动。企业希望通过数字化提高效率、增强灵活性&#xff0c;并为客户提供更好的体验。然而&#xff0c;数字化转型不仅仅涉及技术…

NVR监测软件/设备EasyNVR多品牌NVR管理工具/设备对城市安全有哪些具体益处?

在智慧城市的建设中&#xff0c;各种先进的技术系统正发挥着越来越重要的作用。其中&#xff0c;NVR监测软件/设备EasyNVR作为一种高效的视频边缘计算网关&#xff0c;不仅能够实现视频数据的采集、编码和存储&#xff0c;还能与其他智慧城市系统进行深度集成&#xff0c;共同推…

20241102解决荣品PRO-RK3566开发板刷Rockchip原厂的Buildroot使用荣品的DTS出现

20241102解决荣品PRO-RK3566开发板刷Rockchip原厂的Buildroot使用荣品的DTS出现fiq_debugger问题 2024/11/2 9:46 缘起&#xff1a;给荣品PRO-RK3566开发板刷Rockchip原厂的Buildroot时&#xff0c;DEBUG波特率是1.5Mbps。 但是启动到FIQ阶段&#xff0c;在你使用荣品的DTS的时…

ctfshow文件包含web78~81

目录 web78 方法一&#xff1a;filter伪协议 方法二&#xff1a;input协议 方法三&#xff1a;data协议 web79 方法一:input协议 方法二&#xff1a;data协议 web80 方法一&#xff1a;input协议 方法二&#xff1a;日志包含getshell web81 web78 if(isset($_GET[file]…

电能表预付费系统-标准传输规范(STS)(30)

6.5.3.2 CONTROLBlock construction The 1 6 digit CONTROLBlock is constructed from the data elements in the APDU as defined in Table 36 and Table 37.The most significant digit is in position 1 5 and the least significant digit in position 0. APDU中的数据元素…

基于YOLO11/v10/v8/v5深度学习的维修工具检测识别系统设计与实现【python源码+Pyqt5界面+数据集+训练代码】

《博主简介》 小伙伴们好&#xff0c;我是阿旭。专注于人工智能、AIGC、python、计算机视觉相关分享研究。 ✌更多学习资源&#xff0c;可关注公-仲-hao:【阿旭算法与机器学习】&#xff0c;共同学习交流~ &#x1f44d;感谢小伙伴们点赞、关注&#xff01; 《------往期经典推…

人工智能基础-opencv-图像处理篇

一.图像预处理 图像翻转 cv2.flip 是 OpenCV 库中的一个函数&#xff0c;用于翻转图像。翻转可以是水平翻转、垂直翻转或同时水平和垂直翻转。这个函数接受两个参数&#xff1a;要翻转的图像和一个指定翻转类型的标志。 img cv2.imread(../images/car2.png) #翻转 0&#xf…

Hive学习笔记

1 Hive基本概念 1.1 Hive定义 Hive&#xff1a;由 Facebook 开源用于解决海量结构化日志的数据统计工具。 Hive 是基于 Hadoop 的一个数据仓库工具&#xff0c;可以将结构化的数据文件映射为一张表&#xff0c;并 提供类 SQL 查询功能。 利用MapReduce去查询数据文件中的某些内…

用图说明 CPU、MCU、MPU、SoC 的区别

CPU CPU 负责执行构成计算机程序的指令&#xff0c;执行这些指令所指定的算术、逻辑、控制和输入/输出&#xff08;I/O&#xff09;操作。 MCU (microcontroller unit) 不同的 MCU 架构如下&#xff0c;注意这里的 MPU 表示 memory protection unit MPU (microprocessor un…

HTML 语法规范——代码注释、缩进与格式、标签与属性、字符编码等

文章目录 一、代码注释1.1 使用注释的主要目的1.2 使用建议二、标签的使用2.1 开始标签和结束标签2.2 自闭合标签2.3 标签的嵌套2.4 标签的有效性三、属性四、缩进与格式4.1 一致的缩进4.2 元素单独占用一行4.3 嵌套元素的缩进4.4 避免冗长的行五、字符编码六、小结在开发 HTML…

虚拟现实与增强现实:重塑娱乐和教育的边界!

内容概要 在这个瞬息万变的时代&#xff0c;虚拟现实&#xff08;VR&#xff09;和增强现实&#xff08;AR&#xff09;正如两位魔法师&#xff0c;腾云驾雾间掀起了一场教育与娱乐的革命。虚拟现实带我们飞跃平凡&#xff0c;进入一个充满奇迹的数字宇宙&#xff0c;仿佛我们…

中仕公考:上海市25年公务员考试今日报名

2025年上海市公务员考试于今日开始报名 考试报名采取网络报名方式进行&#xff0c;报考者可在2024年11月2日0:00至11月8日12:00期间登录专题网站进行报名。 年龄在18周岁以上&#xff0c;35周岁以下(1988年11月至2006年11月期间出生)&#xff0c;应届硕士、博士研究生报考的&…

Diving into the STM32 HAL-----HAL_GPIO

1、怎么看待外设&#xff1a; 从总线连接的角度看&#xff0c;外设和Core、DMA通过总线交换数据&#xff0c;正所谓要想富先修路。要注意&#xff0c;这些总线中的每一个都连接到不同的时钟源&#xff0c;这些时钟源决定了连接到该总线的外设操作的最大速度。 从内存分配的角度…

【表格解决问题】EXCEL行数过多,WPS如何按逐行分别打印多个纸张中

1 问题描述 如图&#xff1a;我的表格行数太多了。打印在一张纸上有点不太好看 2 解决方式 Step01&#xff1a;先选中你需要打印的部分&#xff0c;找到【页面】->【打印区域】->【设置打印区域】 Step02&#xff1a;先选中一行&#xff0c;找到【插入分页符】 Step0…

提升安全上网体验:Windows 11 启用 DOH(阿里公共DNS)

文章目录 阿里公共 DNS 介绍免费开通云解析 DNS 服务Windows 编辑 DNS 设置配置 IPv4配置 IPv6 路由器配置 DNS 阿里公共 DNS 介绍 https://alidns.com/ 免费开通云解析 DNS 服务 https://dnsnext.console.aliyun.com/pubDNS 开通服务后&#xff0c;获取 DOH 模板&#xff0…

如何在Linux下部署自己的ZFile开源网盘

ZFile 项目介绍 ZFile是一个功能强大、灵活的开源网盘系统&#xff0c;为用户提供安全便捷的文件存储和共享方案。 项目概述 ZFile由ZFile, Inc.开发和维护&#xff0c;基于Docusaurus构建。其用户友好的界面支持多种文件存储和共享功能&#xff0c;并具备高度的可定制性和扩…

Spring AI : 让ChatGPT成为你构建应用的核心亮点

本文是一篇介绍spring ai的文章&#xff0c;主要介绍了生成文本内容&#xff0c;以及读取图片中内容两个能力。 之所以介绍这两个能力&#xff0c;是因为 大模型目前最适合做的事情有两个&#xff1a; 1&#xff09; 非结构化数据的结构化&#xff08;图片转文字&#xff0c;…

Windows 命令提示符(cmd)中输入 mysql 并收到错误消息“MySQL不是内部或外部命令,也不是可运行的程序或批处理文件?

目录 背景: 过程&#xff1a; 1.找到MySQL安装的路径 2.编辑环境变量 3.打开cmd&#xff0c;输入mysql --version测试成功 总结: 背景: 很早之前安装了Mysql数据库&#xff0c;想查询一下当前安装的MySQL客户端的版本号&#xff0c;我在命令行界面输入mysql --verion命令回…

履带式排爆演习训练机器人技术详解

履带式排爆演习训练机器人是现代反恐、救援及危险环境处理领域中的重要工具。它们结合了先进的机械设计、智能感知、精确控制及高效算法&#xff0c;能够在复杂、危险的环境中执行排爆、侦察、取样等多种高风险任务&#xff0c;极大地保障了人员安全。 技术特点 1. 卓越的地面…

基于SSM医院门诊互联电子病历管理系统的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;用户管理&#xff0c;医生管理&#xff0c;项目分类管理&#xff0c;项目信息管理&#xff0c;预约信息管理&#xff0c;检查信息管理&#xff0c;系统管理 用户账号功能包括&#xff1a;系统首页&…