基于PreparedStatement抓取带参最终SQL(oracle,mysql,PostgreSQL等通用)

前言

很多抓取最终SQL的方法,都是带着?的。比如:


SELECT value from sys_param where name=?

我们现在想把 ? 给去掉。有什么办法呢

方法1 编写工具类

(该方法有些情况下是不适用的,比如oracle数据库,该工具类就实测不生效),oracle或者其它数据库通用的生效方法见方法2.

工具类参考资料来源:https://www.cnblogs.com/wggj/p/12762648.html

工具类代码:


import oracle.jdbc.internal.OraclePreparedStatement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import java.sql.PreparedStatement;
import java.sql.SQLException;public class SqlUtil {public static final Logger logger = LoggerFactory.getLogger(SqlUtil.class);public static String getSql(PreparedStatement ps) {try {if (ps == null || ps.getConnection() == null)return null;switch (ps.getConnection().getMetaData().getDatabaseProductName().toUpperCase()) {case "ORACLE":OraclePreparedStatement ops = (OraclePreparedStatement) ps;ops.getreturn ops.getOriginalSql();case "MYSQL":String temp = ps.toString();return temp.substring(temp.indexOf(':') + 1);case "POSTGRESQL":return ps.toString();}} catch (SQLException e) {logger.error("sql异常", e);return null;}return ps.toString();}
}

如何使用该工具类:


PreparedStatement ps = null;
int count = 0;
int ord = 1;for (Map<String, Object> map : data) {// 示例SQLString sql = "insert into myTest (ORD,INPUT_DATE,LASTUPD_DTM,BILLRECID,YM) values(?,?,?,?,?)";ps = conn.prepareStatement(sql);ps.setInt(1, ord);java.util.Date utilDate = new java.util.Date();java.sql.Date sqlDate = new java.sql.Date(utilDate.getTime());ps.setDate(2, sqlDate);ps.setDate(3, sqlDate);ps.setString(4, param.getDefineName());ps.setString(5, zheJiuParamVO.getJizqj());
//使用该工具类打印最终SQL:logger.error("SQL语句:" + SqlUtil.getSql(ps));ps.executeUpdate();
}

方法1在mysql生效,oracle实测不生效。直接看方法2

方法2:所有数据库(含Oracle)通用方法!

思路是这样的,我们其实要做的事情就是将所有的?替换为具体的参数。所以我们可以从PreparedStatement入手,写一个自定义日志记录的PreparedStatement。

步骤1:建立自定义PreparedStatement

建立一个空类,自行implements其中方法:

public class LoggingPreparedStatement implements PreparedStatement {//自行implements其中方法
}

步骤2:添加一些自定义方法:

读下面代码之前,先解释一下是啥意思:

  • 首先是添加一个 delegate (代理),这个代理只做一件事情,就是this.delegate = con.prepareStatement(sql);,调用原生的 con.prepareStatement(sql) ,这样不管是将来走哪个PrepareStatement 的实现类,都会进行兼容(步骤三还会再次用到这个delegate)
  • 然后就是一个打印sql语句的getSQL方法,这个方法就是咱们要使用的,将来会调用 getSQL 来得到不带 ?
    的最终SQL

public class LoggingPreparedStatement implements PreparedStatement {private PreparedStatement delegate;private String sql;private Map<Integer,Object> parameter = new TreeMap<Integer,Object>();public LoggingPreparedStatement(Connection con, String sql) throws SQLException {this.sql = sql;//代理this.delegate = con.prepareStatement(sql);}//实际打印SQL语句:public String getSQL() {String returnSQL = sql;//TreeMap returns sorted by keyfor(Object o : parameter.values()) {//Replace first ? with the valuereturnSQL = returnSQL.replaceFirst("\\?", o.toString());}return returnSQL;}//下面是自行implements其中方法
//下面是自行implements其中方法
//下面是自行implements其中方法
//下面是自行implements其中方法
// .....
// .....
// .....
}

步骤3:利用delegate (代理) 重写原有的PreparedStatement方法:

示例:

主要做了这样的事情,parameter这个全局变量是我们getSQL()打印要用到的,利用delegate执行原生的PreparedStatement方法的同时,将变量参数塞入 parameter 中。

注意,业务代码中有多少 setXXX,这里就要重写多少个

@Overridepublic void setString(int parameterIndex, String x) throws SQLException {parameter.put(parameterIndex, x);delegate.setString(parameterIndex, x);}
@Overridepublic void setDate(int parameterIndex, Date x) throws SQLException {parameter.put(parameterIndex, x);delegate.setDate(parameterIndex, x);}
@Overridepublic void setObject(int parameterIndex, Object x, int targetSqlType) throws SQLException {parameter.put(parameterIndex, x);delegate.setObject(parameterIndex, x);}

步骤4 修改业务代码,让其适配自定义PreparedStatement并且打印日志

PreparedStatement ps = null;
int count = 0;
int ord = 1;
try {conn.setAutoCommit(false);for (Map<String, Object> map : data) {// 示例SQLString sql = "insert into myTest (ORD,INPUT_DATE,LASTUPD_DTM,BILLRECID,YM) values(?,?,?,?,?)";// 重点!替换PreparedStatement// 重点!替换PreparedStatementps = new LoggingPreparedStatement(conn, sql);
//                ps = conn.prepareStatement(sql);ps.setInt(1, ord);java.util.Date utilDate = new java.util.Date();java.sql.Date sqlDate = new java.sql.Date(utilDate.getTime());ps.setDate(2, sqlDate);ps.setDate(3, sqlDate);ps.setString(4, param.getDefineName());ps.setString(5, zheJiuParamVO.getJizqj());//使用和打印:logger.error("最终SQL:" + ((LoggingPreparedStatement) ps).getSQL());ps.executeUpdate();}

最终效果:

我们看到,即使是Oracle,也能在log中打印不带?的最终SQL:

11:06:03.180 [http-nio-8090-exec-1] ERROR SQL:
insert into myTest(ORD,INPUT_DATE,LASTUPD_DTM,BILLRECID,YM,order_no,zc_code,jqid,zj_month,zc_name,input_user,zj_amt,sz_name,zc_type) values(85834,2023-08-25,2023-08-25,bill.AssetDepreciationBillDefine,202307,202307,TY2023000504,89a79051-c000-0021-597d-4e70c0c5cae0,1,桌面工作站,系统,156.81,教学,2010199)

大功告成!!!!

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

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

相关文章

探索内网穿透工具:实现局域网SQL Server数据库的公网远程访问方法

文章目录 1.前言2.本地安装和设置SQL Server2.1 SQL Server下载2.2 SQL Server本地连接测试2.3 Cpolar内网穿透的下载和安装2.3 Cpolar内网穿透的注册 3.本地网页发布3.1 Cpolar云端设置3.2 Cpolar本地设置 4.公网访问测试5.结语 1.前言 数据库的重要性相信大家都有所了解&…

【mysql事务隔离级别】事务隔离级别(面试高频考点!)

目录 什么是事务的隔离级别&#xff1f; 常见的四种事务隔离级别 读未提交 读已提交 为什么没有解决幻读问题&#xff0c;是怎么导致的&#xff1f; 可重复读 串行化 什么是事务的隔离级别&#xff1f; 事务的隔离级别&#xff08;Isolation Level&#xff09;指的是在并…

下一代存储解决方案:湖仓一体

文章首发地址 湖仓一体是将数据湖和数据仓库相结合的一种数据架构&#xff0c;它可以同时满足大数据存储和传统数据仓库的需求。具体来说&#xff0c;湖仓一体可以实现以下几个方面的功能&#xff1a; 数据集成&#xff1a; 湖仓一体可以集成多个数据源&#xff0c;包括结构…

Spring Cache框架(缓存)

1、介绍&#xff1a; Spring Cache 是一个框架&#xff0c;实现了基于注解的缓存功能&#xff0c;只需要简单加个注解&#xff0c;就能实现缓存功能。它提供了一层抽象&#xff0c;底层可以切换不同的cache实现。具体就是通过CacheManager 接口来实现不同的缓存技术。 针对不同…

Pygame编程(4)event模块

Pygame编程&#xff08;4&#xff09;event模块 函数示例 函数 pygame.event.pump 让 Pygame 内部自动处理事件pygame.event.get 从队列中获取事件pygame.event.poll 从队列中获取一个事件pygame.event.wait 等待并从队列中获取一个事件pygame.event.peek 检测某类型事件是否在…

私有化部署即时通讯平台,30分钟替换钉钉和企业微信

随着企业对即时通讯和协作工具的需求不断增长&#xff0c;私有化部署的即时通讯平台成为企业的首选。WorkPlus作为有10余年行业深耕经验与技术沉淀品牌&#xff0c;以其安全高效的私有化部署即时通讯解决方案&#xff0c;帮助企业在30分钟内替换钉钉和企业微信。本文将深入探讨…

JavaWeb学习-Day10

SpringBootWeb案例 准备工作 开发流程&#xff1a; 开发接口步骤&#xff1a; 删除部门&#xff1a; 新增部门&#xff1a; 简化代码&#xff1a; limit:分页展示&#xff0c;公式&#xff1a;&#xff08;页数-1&#xff09;*页面总数&#xff0c;页面总数 目前出现的问题&am…

STM32 CubeMX (H750)RGB屏幕 LTDC

STM32 CubeMX STM32 RGB888 LTDC STM32 CubeMX一、STM32 CubeMX 设置时钟树LTDC使能设置屏幕参数修改RGB888的GPIO 二、代码部分效果 RGB屏幕线束定义&#xff1a; 一、STM32 CubeMX 设置 时钟树 这里设置的时钟&#xff0c;关于刷新速度 举例子&#xff1a;LCD_CLK24MHz 时…

穿越网络迷雾的神奇通道 - WebSocket详解

WebSocket&#xff0c;作为一项前端技术&#xff0c;已经成为现代Web应用不可或缺的一部分。本文将深入解析WebSocket&#xff0c;介绍其工作原理和用途&#xff0c;并通过简单的代码示例&#xff0c;让你对这个神奇的网络通信协议有更深入的了解。 WebSocket是什么&#xff1…

【大数据知识】大数据平台和数据中台的定义、区别以及联系

数据行业有太多数据名词&#xff0c;例如大数据、大数据平台、数据中台、数据仓库等等。但大家很容易混淆&#xff0c;也很容易产生疑问&#xff0c;今天我们就来简单聊聊大数据平台和数据中台的定义、区别以及联系。 大数据平台和数据中台的定义 大数据平台&#xff1a;一个…

Mysql索引+事务+存储引擎

索引 索引的概念 索引是一个排序的列表&#xff0c;在这个列表中存储着索引的值和包含这个值的数据所在行的物理地址&#xff08;类似于C语言的链表通过指针指向数据记录的内存地址&#xff09;。 使用索引后可以不用扫描全表来定位某行的数据&#xff0c;而是先通过索引表找…

浅谈 Linux 下 vim 的使用

Vim 是从 vi 发展出来的一个文本编辑器&#xff0c;其代码补全、编译及错误跳转等方便编程的功能特别丰富&#xff0c;在程序员中被广泛使用。 Vi 是老式的字处理器&#xff0c;功能虽然已经很齐全了&#xff0c;但还有可以进步的地方。Vim 可以说是程序开发者的一项很好用的工…

代码随想录Day_48打卡

①、打家劫舍 你是一个专业的小偷&#xff0c;计划偷窃沿街的房屋。每间房内都藏有一定的现金&#xff0c;影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统&#xff0c;如果两间相邻的房屋在同一晚上被小偷闯入&#xff0c;系统会自动报警。 给定一个代表每个房…

STM32之17.PWM脉冲宽度调制

一LED0脉冲宽度调制在TIM14_CHI&#xff0c;先将LED&#xff08;PF9&#xff09;代码配置为AF推挽输出模式&#xff0c;将PF9引脚连接到TIM14&#xff0c; #include <stm32f4xx.h>static GPIO_InitTypeDef GPIO_InitStruct;void Led_init(void) {//打开端口F的硬件时钟&a…

Yolo系列-yolov2

YOLO-V2 更快&#xff01;更强&#xff01; YOLO-V2-BatchNormalization BatchNormalization&#xff08;批归一化&#xff09;是一个常用的深度神经网络优化技术&#xff0c;它可以将输入数据进行归一化处理&#xff0c;使得神经网络更容易进行学习。在YOLOv2中&#xff0c;B…

《C和指针》笔记11: external和internal链接属性

当组成一个程序的各个源文件分别被编译之后&#xff0c;所有的目标文件以及那些从一个或多个函数库中引用的函数链接在一起&#xff0c;形成可执行程序。然而&#xff0c;如果相同的标识符出现在几个不同的源文件中时&#xff0c;它们是像Pascal那样表示同一个实体&#xff1f;…

HashSet 和 TreeSet 的区别

HashSet 和 TreeSet 都是 Java 中的集合类&#xff0c;用于存储一组不重复的元素。它们的主要区别在于内部实现和性能方面。 内部实现&#xff1a; HashSet 使用哈希表实现&#xff0c;通过散列函数将元素存储在数组中&#xff0c;这样可以快速地查找元素。TreeSet 使用红黑树实…

Apache StreamPark系列教程第二篇——项目打包和开发

一、项目打包 项目依赖maven、jdk8.0、前端(node、npm) //下载代码 git clone//maven打包相关内容 mvn -N io.takari:maven:wrapper //前端打包相关内容 curl -sL https://rpm.nodesource.com/setup_16.x | bash - yum -y install nodejs npm -v npm install -g pnpm默认是h2…

手把手教你搭建Serv-U FTP服务器共享文件并实现外网远程访问「无公网IP」

文章目录 1. 前言2. 本地FTP搭建2.1 Serv-U下载和安装2.2 Serv-U共享网页测试2.3 Cpolar下载和安装 3. 本地FTP发布3.1 Cpolar云端设置3.2 Cpolar本地设置 4. 公网访问测试5. 结语 1. 前言 科技日益发展的今天&#xff0c;移动电子设备似乎成了我们生活的主角&#xff0c;智能…

Unity字符串性能问题

前言 分享一些通过书籍和网络学到的知识 每次动态创建一个string&#xff0c;C#都会在堆内存分配一个内存用来分配字符串&#xff0c;因为C#没有对字符串的缓存机制&#xff0c;会导致每次连接、切割、组合的时候都会申请新的内存&#xff0c;并且抛弃原来的内存&#xff0c;等…