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

告别MyBatis的‘?‘占位符:用p6spy 3.9.1在Spring Boot里打印可直接执行的SQL(附自定义日志格式)

告别MyBatis的'?'占位符:用p6spy 3.9.1在Spring Boot里打印可直接执行的SQL(附自定义日志格式)

调试SQL语句是Java开发中的日常操作,但MyBatis和JPA等ORM框架输出的预编译SQL总带着恼人的'?'占位符。每次排查问题时,开发者不得不手动替换参数,既浪费时间又容易出错。p6spy 3.9.1正是为解决这一痛点而生——它能将晦涩的预编译语句转换为可直接在数据库客户端执行的完整SQL。

1. 为什么需要SQL语句可视化?

在Spring Boot项目中,ORM框架生成的SQL日志通常是这样的:

SELECT * FROM users WHERE id = ? AND status = ?

这种输出对调试极不友好:

  • 无法直接执行:需要手动替换占位符
  • 缺少关键信息:执行时间、连接ID等调试要素缺失
  • 上下文割裂:难以关联同一事务中的多条SQL

p6spy通过数据源代理技术,在SQL到达真实数据库前进行拦截和格式化。相比原生HikariCP或Druid的监控功能,它的优势在于:

  • 完整的参数替换:自动将?替换为实际值
  • 执行耗时统计:精确到毫秒的性能数据
  • 多输出渠道:控制台、文件或Slf4j日志系统

实际项目中,使用p6spy后SQL日志变为:

[2023-08-20 14:30:45] --- | took 12ms | statement | connection 5 SELECT * FROM users WHERE id = 123 AND status = 'ACTIVE';

2. Spring Boot集成实战

2.1 依赖配置

首先在pom.xml中添加p6spy依赖(注意排除可能冲突的旧版本):

<dependency> <groupId>p6spy</groupId> <artifactId>p6spy</artifactId> <version>3.9.1</version> </dependency>

2.2 数据源改造

修改application.yml中的数据库配置:

spring: datasource: driver-class-name: com.p6spy.engine.spy.P6SpyDriver url: jdbc:p6spy:mysql://localhost:3306/db_name # 原始配置需保留在spy.properties中

2.3 核心配置文件

resources/spy.properties中配置基础参数:

# 启用SQL格式化模块 module.log=com.p6spy.engine.logging.P6LogFactory # 使用控制台输出 appender=com.p6spy.engine.spy.appender.StdoutLogger # 真实数据库驱动 driverlist=com.mysql.cj.jdbc.Driver # 慢SQL阈值(秒) outagedetection=true outagedetectioninterval=2

3. 高级格式化技巧

3.1 内置格式化方案

p6spy提供两种预置格式:

  • SingleLineFormat(默认):基础单行输出
  • CustomLineFormat:可配置的模板化输出

推荐使用CustomLineFormat并配置:

logMessageFormat=com.p6spy.engine.spy.appender.CustomLineFormat customLogMessageFormat=%(currentTime) | 耗时 %(executionTime)ms | 连接 %(connectionId) | 语句:%(sql)

3.2 自定义格式化器

创建自定义格式化类实现更复杂的日志输出:

public class CustomSqlFormatter implements MessageFormattingStrategy { @Override public String formatMessage(int connectionId, String now, long elapsed, String category, String prepared, String sql) { return String.format("\n[%s] [%dms] %s\n%s;", LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_TIME), elapsed, category, sql.replaceAll("\\s+", " ").trim()); } }

然后在配置中指定:

logMessageFormat=com.your.package.CustomSqlFormatter

4. 生产环境最佳实践

4.1 性能优化配置

# 关闭不必要的信息 excludecategories=info,debug,resultset,batch # 仅记录超过100ms的SQL executionThreshold=100 # 异步日志输出(需配合Log4j2) appender=com.p6spy.engine.spy.appender.AsyncLogger

4.2 多环境策略

建议通过Spring Profile实现差异化配置:

# application-dev.yml p6spy: enabled: true log-level: debug # application-prod.yml p6spy: enabled: false

4.3 与Logback集成

对于使用SLF4J的项目,推荐配置:

appender=com.p6spy.engine.spy.appender.Slf4JLogger logMessageFormat=com.p6spy.engine.spy.appender.CustomLineFormat customLogMessageFormat=[%(executionTime)ms] %(sqlSingleLine)

配合logback-spring.xml实现SQL日志单独归档:

<appender name="SQL_APPENDER" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>logs/sql.log</file> <filter class="ch.qos.logback.classic.filter.ThresholdFilter"> <level>DEBUG</level> </filter> </appender> <logger name="p6spy" level="DEBUG" additivity="false"> <appender-ref ref="SQL_APPENDER"/> </logger>

5. 疑难问题排查

问题1:SQL日志没有参数替换

  • 检查driverlist是否配置了真实驱动
  • 确认URL前缀为jdbc:p6spy:

问题2:性能明显下降

  • 启用executionThreshold过滤短耗时SQL
  • 考虑使用AsyncLogger异步输出

问题3:与HikariCP兼容问题

# 在spy.properties中添加 deregisterdrivers=true realdatasourceclass=com.zaxxer.hikari.HikariDataSource

在最近的一个电商项目中,我们通过p6spy发现了多个N+1查询问题。其中一个商品列表接口的SQL调用次数从原来的57次降到了3次,响应时间从1200ms优化到200ms。关键就在于能直观看到完整SQL执行序列。

http://www.jsqmd.com/news/669333/

相关文章:

  • 《uni-app》Checkbox组件实战:从基础配置到跨平台表单交互
  • SX126x CAD参数cadDetPeak/Min怎么调?一份来自官方测试数据的避坑指南
  • SVGSON:企业级SVG-JSON双向转换解决方案助力生产就绪的图形数据处理
  • H3C S5500-SI交换机LLDP配置实战:从零排查网络邻居‘失联’问题
  • 调试LVDS屏别再只盯着代码了!从屏闪、白屏到触摸不准,三个实战问题背后的硬件时序与配置原理
  • STM32F407 DSP实战:用CMSIS-DSP库搞定复数运算(共轭、点乘、求模)
  • C++11时间戳实战:用std::chrono::system_clock构建跨平台时间服务
  • 虚拟机安装Ubuntu 24.04.x及其常用软件(2026.4)
  • 如何在网页中完整显示数组内所有对象的全部属性
  • FM调制解调背后的信号处理魔法:用MATLAB拆解通信原理
  • 别再手动算了!用JavaScript/Node.js实现RGB到HEX颜色转换的三种实用方法
  • SITS2026实测:AGI辅助蛋白质结构预测准确率提升至99.2%,但92%的研究者仍在用错3个关键提示词
  • uni-app本地APK打包实战:从HBuilder X到Android Studio的避坑指南
  • 计算机常用英文词汇概念解释
  • Shared Control【共享控制】- 基于隐式动作学习的辅助机器人直觉化操控
  • Layui表单验证失败时如何修改默认弹出的Tips气泡颜色
  • c#如何添加按钮点击事件_c#添加按钮点击事件的几种常见用法
  • 手把手教你用EJTAG调试龙芯开发板:从硬件连接到GDB远程调试
  • Production Rails扩展架构设计:如何从单体应用到分布式系统的平滑演进
  • Git实战:当.gitignore遇上submodule子仓库,如何避免文件忽略失效的坑?
  • 避坑指南:在Win10上用VS2019编译ITK 5.2和RTK 2.3,我踩过的那些坑都帮你填平了
  • Driver Store Explorer实战:5步实现Windows驱动管理自动化
  • Open UI5 源代码解析之1104:MenuItem.js
  • STM32 IAP升级必备:3分钟搞定Hex文件合并(附常见错误排查)
  • 保姆级教程:在RuoYi-AI里用Ollama跑通本地Llama3模型(附完整配置截图)
  • 题解:AcWing 423 采药
  • CSS开发大型项目如何管理_使用BEM命名规范避免样式冲突
  • AGI自主规划能力认证体系(ISO/IEC 23894-2:2024草案深度解读):含6类强制审计项与21个否决性缺陷清单
  • SSD硬盘对HTML工具速度有影响吗_存储介质与开发效率关系【详解】
  • Python多进程编程:从阻塞到异步,掌握apply与apply_async的核心差异与实践