JDBC使用p6spy记录实际执行SQL方法【解决SQL打印两次问题】

embedded/2024/11/24 17:55:27/

p6spy介绍

p6spy 是一个开源的 JDBC 数据源代理工具,主要用于拦截和记录应用程序与数据库之间的所有 SQL 操作,方便开发者进行 SQL 调试、性能监控和问题排查。
p6spy可以打印实际执行的sql,在开发过程中方便调试,和使用框架无关,Mybatis和Hibernate等都可以使用。

使用方法

1.引入p6spy依赖
        <dependency><groupId>p6spy</groupId><artifactId>p6spy</artifactId><version>3.9.1</version></dependency>
2.修改数据库连接信息,这里我使用的是Druid连接池
      oracleDataSource = new DruidDataSource();// 替换jdbcUrl为p6spy代理格式
//        oracleDataSource.setUrl("jdbc:oracle:thin:@10.5.1.22:1521/ORCL");oracleDataSource.setUrl("jdbc:p6spy:oracle:thin:@10.5.1.22:1521/ORCL");oracleDataSource.setUsername("ORACLE_USERNAME");oracleDataSource.setPassword("ORACLE_PASSWORD");// 指定p6spy的代理类oracleDataSource.setDriverClassName("com.p6spy.engine.spy.P6SpyDriver");
3.在resource目录下创建配置文件spy.properties
# 指定日志实现,这里使用 SLF4J 输出日志
appender=com.p6spy.engine.spy.appender.Slf4JLogger# 配置日志格式处理器,默认为SingleLineFormat是单行输出,这里指定多行日志输出格式,增加可读性
logMessageFormat=com.p6spy.engine.spy.appender.MultiLineFormat
4. 控制台成功打印日志
10:58:53.784 [main] INFO p6spy - #1732071533784 | took 1ms | statement | connection 4| url jdbc:p6spy:oracle:thin:@10.5.1.22:1521/ORCL
SELECT 1 FROM DUAL
SELECT 1 FROM DUAL;
10:58:53.798 [main] INFO p6spy - #1732071533798 | took 12ms | statement | connection 4| url jdbc:p6spy:oracle:thin:@10.5.1.22:1521/ORCL
SELECT * FROM cms_mid_table WHERE table_module='CIF'
SELECT * FROM cms_mid_table WHERE table_module='CIF';

SQL打印两遍

原因

查看日志格式处理器代码,很简单,就是将参数组装成打印结果

public class MultiLineFormat implements MessageFormattingStrategy {@Overridepublic String formatMessage(final int connectionId, final String now, final long elapsed, final String category, final String prepared, final String sql, final String url) {return "#" + now + " | took " + elapsed + "ms | " + category + " | connection " + connectionId + "| url " + url + "\n" + prepared + "\n" + sql +";";}
}

我在代码中是使用apache工具类QueryRunner#excute直接执行sql,没有使用预编译参数

    public void test()  {QueryRunner queryRunner = new QueryRunner();try {queryRunner.execute("DELETE FROM cms_mid_table WHERE table_module='CIF'", ETLConnectionPool.getObConnection());} catch (SQLException e) {throw new RuntimeException(e);}}

QueryRunner内部使用的是PrepareStatement的子类CallableStatement

    private int execute(Connection conn, boolean closeConn, String sql, Object... params) throws SQLException {if (conn == null) {throw new SQLException("Null connection");}if (sql == null) {if (closeConn) {close(conn);}throw new SQLException("Null SQL statement");}CallableStatement stmt = null;int rows = 0;try {stmt = this.prepareCall(conn, sql);this.fillStatement(stmt, params);stmt.execute();rows = stmt.getUpdateCount();this.retrieveOutParameters(stmt, params);} catch (SQLException e) {this.rethrow(e, sql, params);} finally {close(stmt);if (closeConn) {close(conn);}}return rows;}

因为我是直接执行的具体的SQL,所以预编译对象和实际直接的sql相同,导致打印结果看上去是相同的SQL打印了两遍。

解决方法

  1. 创建自定义日志格式化类
@Slf4j
public class CustomLogFormatter implements MessageFormattingStrategy {@Overridepublic String formatMessage(int connectionId, String now, long elapsed, String category, String prepared, String sql, String url) {// 自定义sql日志内容return "#" + now + " | took " + elapsed + "ms | connection " + connectionId + "| " + url + "\n"+ (sql.startsWith("\n") ? "" : "\n") + sql.replaceAll(" {2,}", " ") + (sql.endsWith(";") ? "" : ";")+ "\n--------------------------------------------------------------------------------------------\n";}
}
  1. 在spy.properties配置文件中指定自定义格式类
# 使用 SLF4J 输出日志
appender=com.p6spy.engine.spy.appender.Slf4JLogger
# 使用自定义日志格式处理器
logMessageFormat=com.dhcc.utils.CustomLogFormatter
#logMessageFormat=com.p6spy.engine.spy.appender.MultiLineFormat
#logMessageFormat=com.p6spy.engine.spy.appender.SingleLineFormat

http://www.ppmy.cn/embedded/140169.html

相关文章

EF Core学习笔记二

一、新建.net core的控制台项目 二、安装Entity Framework Core 我们使用的数据库是Sqlite,所以我们需要的程序包是Microsoft.EntityFrameworkCore.Sqlite。 如果想了解更多EF Core NuGet包,请参考:EF Core NuGet 包 | Microsoft Learn Install-Package Microsoft.Entit…

C#里怎么样使用线程暂停?

C#里怎么样使用线程暂停? 如果一个线程没有任务在处理,并且又不进行暂停, 这时候,这个线程就会把当前这个CPU占满,即是所谓的死循环。 因此我们设计线程时,一定要知道线程在什么时候没有工作处理时, 就需要进入等待状态,不能再进行下去,否则会导致死循环, 只是耗费…

信息安全设计第11周预习报告

文章目录 密码系统设计学习内容AI 对学习内容的总结&#xff08;1分&#xff09;要求总结1. 网络基础2. 套接字&#xff08;Sockets&#xff09;3. 客户端连接4. 数据传输5. 并发处理6. 实例&#xff1a;敲门协议服务器7. 实例&#xff1a;HTTP客户端8. 错误处理 9. 端口和协议…

现代密码学

概论 计算机安全的最核心三个关键目标&#xff08;指标&#xff09;/为&#xff1a;保密性 Confidentiality、完整性 Integrity、可用性 Availability &#xff0c;三者称为 CIA三元组 数据保密性&#xff1a;确保隐私或是秘密信息不向非授权者泄漏&#xff0c;也不被非授权者使…

信号signal

信号&#xff0c;signal 信号是一种软件中断机制&#xff0c;基于该机制能实现进程间通信&#xff0c;故信号是实现进程通信的一种方式。 信号由内核线程或者用户线程发起生成&#xff0c;例如&#xff0c;SIGINT 信号通常由用户通过按下 CtrlC 产生。 内核中会保存每个进程的…

安宝特方案 | AR助力紧急救援,科技守卫生命每一刻!

在生死时速的紧急救援战场上&#xff0c;每一秒都至关重要&#xff01;随着科技的发展&#xff0c;增强现实&#xff08;AR&#xff09;技术正在逐步渗透到医疗健康领域&#xff0c;改变着传统的医疗服务模式。 安宝特AR远程协助解决方案&#xff0c;凭借其先进的技术支持和创新…

Windows上杀掉指定端口的进程(批处理脚本方式)

前言 在Windows操作系统中&#xff0c;有时我们可能需要停止占用特定端口的程序&#xff0c;例如当您想要启动一个服务却发现所需端口已被其他程序占用时。手动查找并终止这些进程可能既耗时又容易出错。为了简化这一过程&#xff0c;我们可以编写一个批处理脚本来自动完成这项…

性能监控利器:Ubuntu 22.04 上的 Zabbix 安装与配置指南

简介 今天我们来聊聊如何在 Ubuntu 22.04 上安装和配置 Zabbix。我们会用到 PostgreSQL 作为数据库后端&#xff0c;Nginx 作为 Web 服务器&#xff0c;并用 Let’s Encrypt SSL 证书来保驾护航。 什么是 Zabbix&#xff1f; Zabbix 是一个开源的网络监控和管理解决方案&…