🥂(❁´◡`❁)您的点赞👍➕评论📝➕收藏⭐是作者创作的最大动力🤞

💖📕🎉🔥 支持我:点赞👍+收藏⭐️+留言📝欢迎留言讨论

🔥🔥🔥(源码 + 调试运行 + 问题答疑)🔥🔥🔥  有兴趣可以联系我

🔥🔥🔥  文末有往期免费源码,直接领取获取(无删减,无套路)

MyBatis SQL日志输出全解析:从基础实现到高级优化

热门标题推荐

  1. MyBatis SQL日志输出深度剖析:从基础实现到插件优化

  2. 手把手实现MyBatis SQL日志功能:原理、实现与陷阱规避

  3. MyBatis SQL监控终极指南:掌握SQL执行的每一个细节

  4. 深入理解MyBatis日志机制:如何优雅输出可执行的SQL语句

  5. MyBatis SQL日志输出全攻略:从简单打印到高级定制

正文

SQL日志输出是MyBatis开发中最常用且重要的功能之一,它帮助开发者监控和调试数据库操作。一个良好的SQL日志系统不仅能显示执行的SQL语句,还能展示参数值、执行时间等关键信息。本文将深入探讨MyBatis中SQL日志输出的实现原理、各种实现方式及其优缺点。

一、SQL日志输出的重要性

在开发过程中,SQL日志输出具有不可替代的价值:

  1. 调试与排错:当SQL执行出现问题时,日志可以帮助快速定位问题

  2. 性能监控:通过记录SQL执行时间,识别性能瓶颈

  3. 审计追踪:记录所有数据库操作,满足审计需求

  4. SQL优化:分析实际执行的SQL,进行优化调整

二、基础SQL日志输出实现

最简单的SQL日志输出可以在StatementHandler.prepare方法之后或Executor执行SQL之前实现:

public class SimpleStatementHandler implements StatementHandler {@Overridepublic Statement prepare(Connection connection) throws SQLException {// 原始prepare逻辑Statement statement = connection.createStatement();// 输出SQL日志String sql = boundSql.getSql();System.out.println("Executing SQL: " + sql);return statement;}}

这种方式虽然简单,但只能输出带有占位符(?)的SQL,无法显示实际的参数值。

三、带参数值的SQL日志输出

要输出完整的可执行SQL,需要获取参数值并替换到SQL中的占位符:

public class ParameterAwareStatementHandler implements StatementHandler {@Overridepublic int update(Statement statement) throws SQLException {// 获取SQL和参数String sql = boundSql.getSql();Object parameter = boundSql.getParameterObject();List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();// 构建完整SQLString completeSql = buildCompleteSql(sql, parameter, parameterMappings);System.out.println("Executing SQL: " + completeSql);// 执行原始逻辑return statement.executeUpdate(sql);}private String buildCompleteSql(String sql, Object parameter, List<ParameterMapping> parameterMappings) {if (parameterMappings == null || parameterMappings.isEmpty()) {return sql;}String completeSql = sql;for (int i = 0; i < parameterMappings.size(); i++) {ParameterMapping mapping = parameterMappings.get(i);Object value = getParameterValue(parameter, mapping);completeSql = completeSql.replaceFirst("\\?", formatParameterValue(value));}return completeSql;}}

注意:这种方法仅用于日志输出,不能直接执行拼接后的SQL,因为存在SQL注入风险。

四、SQL注入风险与安全处理

在拼接SQL参数时,必须注意安全性问题:

  1. 字符串值需要引号包裹:字符串参数必须用单引号包裹

  2. 特殊字符转义:包含单引号的字符串需要转义处理

  3. NULL值处理:NULL值应该直接替换为NULL关键字

  4. 日期格式处理:日期类型需要转换为合适的字符串格式

 private String formatParameterValue(Object value) {if (value == null) {return "NULL";}if (value instanceof String) {return "'" + ((String) value).replace("'", "''") + "'";}if (value instanceof Date) {return "'" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format((Date) value) + "'";}if (value instanceof Number) {return value.toString();}return "'" + value.toString().replace("'", "''") + "'";}

五、基于插件的优雅实现

使用MyBatis插件机制可以实现更优雅、非侵入式的SQL日志输出:

1. 定义日志插件
 @Intercepts({@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})})public class SqlLogPlugin implements Interceptor {private static final Logger logger = LoggerFactory.getLogger(SqlLogPlugin.class);@Overridepublic Object intercept(Invocation invocation) throws Throwable {MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];Object parameter = invocation.getArgs()[1];BoundSql boundSql = mappedStatement.getBoundSql(parameter);String sql = boundSql.getSql();List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();// 构建完整SQLString completeSql = buildCompleteSql(sql, parameter, parameterMappings);long start = System.currentTimeMillis();Object result = invocation.proceed();long end = System.currentTimeMillis();logger.info("SQL: {} | Time: {}ms", completeSql, (end - start));return result;}}
2. 配置插件

在MyBatis配置文件中注册插件:

 <plugins><plugin interceptor="com.example.SqlLogPlugin"><property name="logLevel" value="DEBUG"/></plugin></plugins>

六、高级SQL日志功能

除了基本的SQL输出,还可以实现更高级的日志功能:

1. 执行时间监控
 public class PerformanceMonitorPlugin implements Interceptor {@Overridepublic Object intercept(Invocation invocation) throws Throwable {long start = System.currentTimeMillis();Object result = invocation.proceed();long end = System.currentTimeMillis();long threshold = 1000; // 1秒阈值if (end - start > threshold) {logger.warn("Slow SQL detected: {}ms", (end - start));}return result;}}
2. SQL格式化输出

对于复杂的SQL,格式化输出更易于阅读:

 private String formatSql(String sql) {// 简单的SQL格式化逻辑sql = sql.replaceAll("(?i)select", "\nSELECT ").replaceAll("(?i)from", "\nFROM ").replaceAll("(?i)where", "\nWHERE ").replaceAll("(?i)join", "\nJOIN ").replaceAll("(?i)order by", "\nORDER BY ").replaceAll("(?i)group by", "\nGROUP BY ");return sql;}
3. 敏感数据脱敏

对于包含敏感信息的SQL,需要进行脱敏处理:

 private String maskSensitiveData(String sql) {// 脱敏手机号sql = sql.replaceAll("1[3-9]\\d{9}", "1**********");// 脱敏身份证号sql = sql.replaceAll("\\d{17}[\\dXx]", "***************");// 脱敏银行卡号sql = sql.replaceAll("\\d{16,19}", "*******************");return sql;}

七、集成日志框架

直接使用System.out.println不是生产环境的最佳选择,应该集成成熟的日志框架:

1. SLF4J集成
public class Slf4jSqlLogger {private static final Logger logger = LoggerFactory.getLogger("SQL_LOGGER");public void logSql(String sql, long executionTime, Object... params) {if (logger.isDebugEnabled()) {StringBuilder logMessage = new StringBuilder();logMessage.append("SQL: ").append(sql);logMessage.append(" | Parameters: ").append(Arrays.toString(params));logMessage.append(" | Time: ").append(executionTime).append("ms");logger.debug(logMessage.toString());}}
}
2. 动态日志级别控制

通过配置动态控制SQL日志的详细程度:

 public enum SqlLogLevel {NONE,    // 不输出日志BASIC,   // 只输出SQL语句PARAMS,  // 输出SQL和参数PERFORMANCE, // 输出SQL、参数和执行时间FULL     // 输出所有信息}

八、生产环境最佳实践

在生产环境中使用SQL日志时,应注意以下最佳实践:

  1. 性能影响:日志输出可能影响性能,应在必要时开启

  2. 日志级别控制:根据环境动态调整日志级别

  3. 敏感信息保护:对敏感数据进行脱敏处理

  4. 日志轮转:配置适当的日志轮转策略,避免日志文件过大

  5. 监控告警:对慢SQL和异常SQL设置监控告警

九、常见问题与解决方案

1. 日志输出不完整

问题:参数过多时日志输出被截断 解决方案:限制参数输出长度,或提供摘要信息

2. 性能开销过大

问题:日志输出导致性能显著下降 解决方案:使用异步日志输出,或采样输出部分SQL

3. 特殊类型处理

问题:Blob、Clob等特殊类型无法正常输出 解决方案:对这些类型提供特殊处理,输出摘要信息而非完整内容

十、总结

SQL日志输出是MyBatis开发中不可或缺的功能,从最简单的System.out.println到基于插件的完整解决方案,每种方式都有其适用场景。在实际项目中,应根据具体需求选择合适的实现方式,并注意安全性、性能和可维护性等方面的考虑。

通过本文的介绍,相信您已经对MyBatis SQL日志输出有了全面的了解,能够根据项目需求实现适合的SQL日志功能,从而更好地监控和优化数据库操作。


往期免费源码 (无删减,无套路):🔥🔥🔥  

https://pan.baidu.com/s/1sjAr08PU9Xe7MQf1gjGM5w?pwd=6666​

「在线考试系统源码(含搭建教程)」 (无删减,无套路):🔥🔥🔥  

链接:https://pan.quark.cn/s/96c4f00fdb43 提取码:WR6M
往期免费源码对应视频:

免费获取--SpringBoot+Vue宠物商城网站系统

🥂(❁´◡`❁)您的点赞👍➕评论📝➕收藏⭐是作者创作的最大动力🤞

💖📕🎉🔥 支持我:点赞👍+收藏⭐️+留言📝欢迎留言讨论

🔥🔥🔥(源码 + 调试运行 + 问题答疑)

🔥🔥🔥  有兴趣可以联系我

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

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

相关文章

Spring Boot 监控实战:集成 Prometheus 与 Grafana,打造全方位监控体系

前言 在当今微服务架构盛行的时代&#xff0c;应用程序的监控变得尤为重要。Spring Boot 作为广泛使用的微服务框架&#xff0c;其监控需求也日益增加。Prometheus 和 Grafana 作为开源监控领域的佼佼者&#xff0c;为 Spring Boot 应用提供了强大的监控能力。本文将详细介绍如…

JS中的多线程——Web Worker

众所周知&#xff0c;JavaScript 是单线程运行的&#xff08;至于为什么是单线程可以看一下这篇文章——事件循环机制&#xff09;&#xff0c;当浏览器主线程被大量计算任务阻塞时&#xff0c;页面就会出现明显的卡顿现象。Web Worker 提供了在独立线程中运行 JavaScript 的能…

【SQL注入】延时盲注

sleep(n)​​: 核心延时函数。使数据库程序暂停 n秒。​​if(condition, true_expr, false_expr)​​: 条件判断函数。如果 condition为真&#xff0c;执行 true_expr&#xff0c;否则执行 false_expr。​​用于将延时与判断条件绑定​​。​​mid(a, b, c)​​: 字符串截取函数…

IntelliJ IDEA 2025.1 Java Stream Debugger 快速使用指南

1. 功能概览 Java Stream Debugger 提供 Trace Current Stream Chain 功能&#xff0c;用来在调试时分析和可视化 Stream 操作链。 主要用途&#xff1a; 在运行时查看流操作链的每一步输出找出 map/filter 等操作的问题避免手动加 peek() 打印调试2. 使用入口 在 IDEA 2025.1 …

ARM-指令集全解析:从基础到高阶应用

一、ARM 指令集体系结构版本ARM 公司定义了多个指令集版本&#xff1a;ARMv1&#xff1a;原型机 ARM1&#xff0c;没有用于商业产品。ARMv2&#xff1a;扩展 V1&#xff0c;包含 32 位乘法指令和协处理器指令。ARMv3&#xff1a;第一个微处理器 ARM6 核心&#xff0c;支持 Cach…

第3讲 机器学习入门指南

近年来&#xff0c;随着企业和个人生成的数据量呈指数级增长&#xff0c;机器学习已成为日益重要的技术领域。从自动驾驶汽车到流媒体平台的个性化推荐&#xff0c;机器学习算法已广泛应用于各个场景。让我们深入解析机器学习的核心要义。3.1 机器学习定义机器学习是人工智能的…

深入理解跳表:多层索引加速查找的经典实现

跳表&#xff08;Skip List&#xff09;是一种多层有序链表结构&#xff0c;通过引入多级索引加速查找&#xff0c;其核心设计类似于“立体高速公路系统”&#xff0c;底层是原始链表&#xff0c;上面有各种高度的"高架桥"。 高层道路跨度大&#xff0c;连接远方节点…

Flutter 视频播放器——flick_video_player 介绍与使用

在移动端应用中&#xff0c;视频播放是一个常见的功能场景&#xff0c;例如短视频、直播、课程、广告展示等。 Flutter 本身并没有直接提供视频播放器组件&#xff0c;而是依赖第三方库来实现。 今天要介绍的库是 flick_video_player&#xff0c;它基于 video_player 封装&…

编写cmakelists文件常用语句

cmake_minimum_required (VERSION 3.10) 指定最小版本project(XXXX) 指定项目名字 ---------------set(MAIN_EXEC_NAME dwarf_parser) 定义变量${ MAIN_EXEC_NAME } 变量取值set(CMAKE_CXX_STANDARD 14) 指定c14标准&#xff0c;还有11、17、20等标准…

麒麟桌面系统找不到mbr启动,并重新安装grub

根据你提供的情况,“麒麟桌面系统找不到MBR启动”,这通常是由于GRUB引导损坏、MBR记录丢失或分区表异常导致的。你可以按照以下步骤重新安装GRUB并修复MBR启动: ✅ 步骤一:准备工具 使用银河麒麟LiveCD或U盘启动盘(可用Ventoy制作); 启动电脑,选择从U盘或光盘进入Live环…

【音频字幕】构建一个离线视频字幕生成系统:使用 WhisperX 和 Faster-Whisper 的 Python 实现

一、背景介绍 对于一端没有字幕外国视频、字幕&#xff0c;在不懂外语的情况下&#xff0c;怎么获取相关内容&#xff1f;作为技术宅&#xff0c;怎么自建搭建一个语音转文字的环境当前AI技术这么发达&#xff1f; 试试 二、系统设计 音频提取(仅仅是视频需要该逻辑、本身就是音…

Linux ALSA架构:PCM_OPEN流程 (二)

一 应用端源码路径: external\tinyalsa\pcm.c external\tinyalsa\pcm_hw.cstruct pcm *pcm_open(unsigned int card, unsigned int device,unsigned int flags, struct pcm_config *config) {...pcm->ops &hw_ops;pcm->fd pcm->ops->open(card, device,…

tp5的tbmember表闭包查询 openid=‘abc‘ 并且(wx_unionid=null或者wx_unionid=‘‘)

闭包查询 tbmember表闭包查询查询 openid‘abc并且islose0并且islogout0并且&#xff08;wx_unionidnull或者wx_unionid’&#xff09; Db::table(tbmember)->where([openid>abc,islose>0,islogout>0])->where(function ($query){$query->where(wx_unioni…

邪修实战系列(3)

1、第一阶段邪修实战总览&#xff08;9.1-9.30&#xff09; 把第一阶段&#xff08;基础夯实期&#xff09;的学习计划拆解成极具操作性的每日行动方案。这个计划充分利用我“在职学习”的特殊优势&#xff0c;强调“用输出倒逼输入”&#xff0c;确保每一分钟的学习都直接服务…

【GD32】ROM Bootloader、自定义Bootloader区别

Bootloader是应用程序跑起来之前&#xff0c;用于初始化的一段程序&#xff0c;它分为两种&#xff0c;ROM Bootloader、自定义Bootloader。GD32芯片出厂时预烧录在ROM中的Bootloader&#xff08;以下简称ROM Bootloader&#xff09;和自己编写的Bootloader&#xff08;以下简称…

Linux防火墙-Firewalld

一、 概述 按表现形式划分&#xff1a; 软件防火墙&#xff1a; 集成在系统内部&#xff0c;Linux系统&#xff1a; iptables、firewalld、ufw&#xff1b; windows系统下&#xff1a; windows defender 硬件防火墙&#xff1a; 华为防火墙、思科防火墙、奇安信防火墙、深信服防…

【Qt】PyQt、原生QT、PySide6三者的多方面比较

目录 引言 一、基本定义 二、核心对比维度 1. 编程语言与开发效率 2. 功能与 API 兼容性 3. 性能表现 4. 许可证与商业使用 5. 社区与文档支持 三、迁移与兼容性 四、适用场景推荐 五、总结对比表 总结 引言 PySide6、PyQt&#xff08;通常指 PyQt5/PyQt6&#xf…

JavaWeb站内信系统 - 技术设计文档

1. 系统概述1.1 项目背景本系统旨在为企业或社区平台提供一套完整的站内信解决方案&#xff0c;支持用户之间的消息发送、接收、管理等功能&#xff0c;提升用户间的沟通效率。1.2 设计目标实现用户间消息发送和接收支持一对一和一对多消息发送提供消息状态跟踪&#xff08;已读…

Java基础 9.10

1.System类常见方法和案例exit&#xff1a;退出当前程序arraycopy&#xff1a;复制数组元素&#xff0c;比较适合底层调用&#xff0c;一般使用 Arrays.copyOf 完成复制数组int[] src{1,2,3};int[] dest new int[3]; System.arraycopy(src, 0, dest, 0, 3);currentTimeMilens&…

详解flink性能优化

1. 简介 Apache Flink是一个强大的流处理框架&#xff0c;其性能很大程度上取决于内存的使用效率。在大规模数据处理场景中&#xff0c;合理的内存配置和优化可以显著提升Flink作业的性能和稳定性。本文将深入探讨Flink内存优化的各个方面&#xff0c;包括状态后端选择、内存配…