前言
很多抓取最终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)
大功告成!!!!