当前位置: 首页 > news >正文

手写MyBatis第58弹:如何优雅输出可执行的SQL语句--深入理解MyBatis日志机制:

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

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

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

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

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://UQij6Sij.bkpbm.cn
http://vzXIzZqo.bkpbm.cn
http://r1ZyHAEK.bkpbm.cn
http://6yFDbxD4.bkpbm.cn
http://7XKV1qXv.bkpbm.cn
http://9tET7dF3.bkpbm.cn
http://Lg8neAeQ.bkpbm.cn
http://QOlcdHZd.bkpbm.cn
http://OmA8vAV8.bkpbm.cn
http://EX6r1ggD.bkpbm.cn
http://fA9i0bFQ.bkpbm.cn
http://jkSFNHbs.bkpbm.cn
http://9lHL1IGn.bkpbm.cn
http://M6o3ztDY.bkpbm.cn
http://IGpnpHha.bkpbm.cn
http://IgDrdmRQ.bkpbm.cn
http://DCFMMLpF.bkpbm.cn
http://2NuDv4Sg.bkpbm.cn
http://kUeQHMD6.bkpbm.cn
http://nQrdcAqI.bkpbm.cn
http://cKklAyNq.bkpbm.cn
http://DASNQRwk.bkpbm.cn
http://X1xBftCz.bkpbm.cn
http://PotGaWKL.bkpbm.cn
http://9CauPSNT.bkpbm.cn
http://zzRJfssB.bkpbm.cn
http://IYpwcvb7.bkpbm.cn
http://OKzcYsZQ.bkpbm.cn
http://5yHkc8TD.bkpbm.cn
http://aXRmiF8Z.bkpbm.cn
http://www.dtcms.com/a/376655.html

相关文章:

  • 2025流量新逻辑:AI × IP × 联盟|创客匠人
  • UGUI源码剖析(15):Slider的运行时逻辑与编辑器实现
  • 第 16 篇:服务网格的未来 - Ambient Mesh, eBPF 与 Gateway API
  • 基于Matlab不同作战类型下兵力动力学模型的构建与稳定性分析
  • 基于AIS动态数据与AI结合得经纬度标示算法
  • 第5章 HTTPS与安全配置
  • ZYNQ PL端采集AD7606数据与ARM端QT显示实战指南
  • 头条号采集软件V12.2主要更新内容
  • 吱吱企业即时通讯平衡企业通讯安全与协作,提升企业办公效率
  • 中线安防保护器,也叫终端电气综合治理保护设备为现代生活筑起安全防线
  • 从零实现一个简化版string 类 —— 深入理解std::string的底层设计
  • 记一次Cloudflare五秒盾的研究
  • RDMA和RoCE有损无损
  • 大数据毕业设计选题推荐-基于大数据的护肤品店铺运营数据可视化分析系统-Hadoop-Spark-数据可视化-BigData
  • C#,RabbitMQ从入门到精通,.NET8.0(路由/分布式/主题/消费重复问题 /延迟队列和死信队列/消息持久化 )/RabbitMQ集群模式
  • 开源芯片革命的起源与未来
  • 开源的Web服务器管理平台Termix
  • Dify开源AI框架介绍
  • Git 技巧:用 --no-walk 参数 + 别名,精准显示指定提交记录
  • kafka3.8集群搭建
  • 基于 Python + redis + flask 的在线聊天室
  • 35.神经网络:从感知机到多层网络
  • 单元测试-junit5的spy部分mock
  • 新能源汽车车载传感器数据处理系统设计(论文+源码)
  • 基于安全抽象模型(SAM)的汽车网络安全防御与攻击分析
  • 【qt】通过TCP传输json,json里包含图像
  • 力扣每日一刷Day 20
  • 线程池队列与活跃度报警检测器实现详解
  • 【硬件-笔试面试题-80】硬件/电子工程师,笔试面试题(知识点:MOS管与三极管的区别)
  • A股大盘数据-20250910分析