admin管理员组文章数量:1794759
利用JDBC的PrepareStatement打印真实SQL的方法详解
前言
本文主要给大家介绍了关于利用JDBC的PrepareStatement打印真实SQL的相关内容,分享出来供大家参考学习,下面来一起看看详细的介绍:
我们知道,JDBC 的 PrepareStatement 优点多多,通常都是推荐使用 PrepareStatement 而不是其基类 Statment。PrepareStatement 支持 ? 占位符,可以将参数按照类型转自动换为真实的值。既然这一过程是自动的,封装在 JDBC 内部的,那么我们外部就不得而知目标的 SQL 最终生成怎么样——于是在调试过程中便有一个打印 SQL 的问题。我们对 PrepareStatement 传入 SQL 语句,如 SELECT * FROM table WHERE id = ?,然后我们传入对应的 id 参数,假设是 id = 10,那怎么把得到参数的 SELECT * FROM table WHERE id = 12 结果完整地得出来呢?——这便是本文所要探讨的问题。下面话不多说了,来一起看看详细的介绍:
方法如下:
首先,我们看看典型的一个 PrepareStatement 调用方法,如下一个函数,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | /** * 查询单个结果,保存为 Map<String, Object> 结构。如果查询不到任何数据返回 null。 * * @param conn * 数据库连接对象 * @param sql * SQL 语句,可以带有 ? 的占位符 * @param params * 插入到 SQL 中的参数,可单个可多个可不填 * @return Map<String, Object> 结构的结果。如果查询不到任何数据返回 null。 */ public static Map<String, Object> query(Connection conn, String sql, Object... params) { Map<String, Object> map = null; printRealSql(sql, params); // 打印真实 SQL 的函数
try (PreparedStatement ps = conn.prepareStatement(sql);) { if(params != null) for (int i = 0; i < params.length; i++) ps.setObject(i + 1, params[i]);
try (ResultSet rs = ps.executeQuery();) { if (rs.isBeforeFirst()) { map = getResultMap(rs); } else { LOGGER.info("查询 SQL:{0} 没有符合的记录!", sql); } } } catch (SQLException e) { LOGGER.warning(e); }
return map; } |
值得注意该函数里面:
1 | printRealSql(sql, params); // 打印真实 SQL 的函数 |
其参数一 sql 就是类似 SELECT * FROM table WHERE id = ? 的语句,参数二 params 为 Object... params 的参数列表,可以是任意类似的合法 SQL 值。最后,通过 printRealSql 函数最终得出形如 SELECT * FROM table WHERE id = 12 的结果。
printRealSql 函数源码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | /** * 在开发过程,SQL语句有可能写错,如果能把运行时出错的 SQL 语句直接打印出来,那对排错非常方便,因为其可以直接拷贝到数据库客户端进行调试。 * * @param sql * SQL 语句,可以带有 ? 的占位符 * @param params * 插入到 SQL 中的参数,可单个可多个可不填 * @return 实际 sql 语句 */ public static String printRealSql(String sql, Object[] params) { if(params == null || params.length == 0) { LOGGER.info("The SQL is------------>\\n" + sql); return sql; }
if (!match(sql, params)) { LOGGER.info("SQL 语句中的占位符与参数个数不匹配。SQL:" + sql); return null; }
int cols = params.length; Object[] values = new Object[cols]; System.arraycopy(params, 0, values, 0, cols);
for (int i = 0; i < cols; i++) { Object value = values[i]; if (value instanceof Date) { values[i] = "'" + value + "'"; } else if (value instanceof String) { values[i] = "'" + value + "'"; } else if (value instanceof Boolean) { values[i] = (Boolean) value ? 1 : 0; } }
String statement = String.format(sql.replaceAll("\\\\?", "%s"), values);
LOGGER.info("The SQL is------------>\\n" + statement);
ConnectionMgr.addSql(statement); // 用来保存日志
return statement; }
/** * ? 和参数的实际个数是否匹配 * * @param sql * SQL 语句,可以带有 ? 的占位符 * @param params * 插入到 SQL 中的参数,可单个可多个可不填 * @return true 表示为 ? 和参数的实际个数匹配 */ private static boolean match(String sql, Object[] params) { if(params == null || params.length == 0) return true; // 没有参数,完整输出
Matcher m = Patternpile("(\\\\?)").matcher(sql); int count = 0; while (m.find()) { count++; }
return count == params.length; } |
可见,上述思路是非常简单的,——有多少个 ? 占位符,就要求有多少个参数,然后一一对照填入(数组)。match 函数会检查第一个步骤,检查个数是否匹配,否则会返回“SQL 语句中的占位符与参数个数不匹配”的提示;然后,参数的值会被转换为符合 SQL 值所要求的类型;最后,就是将 SQL 一一填入,——此处使用了一个字符串的技巧,先把 ? 字符通通转换为 %s,——那是 String.format 可识别的占位符,如此再传入 Object[] 参数列表,即可得出我们期待的 SQL 结果。
我们不能保证那 SQL 可以直接放到数据库中被解析。因为我们的初衷只是把 SQL 打印出来,务求更近一步让程序员在开发阶段看到 SQL 是怎么样子的,而且不是一堆 ?、?……,这样会显得更符合真实情形一点。
PrepareStatement 内部源码肯定有这一步骤或者某个变量是表示那个真实 SQL 的,——只是没有暴露出来。如果有,那么对程序员会更友好一些。
本文标签: 详解真实方法JDBCPrepareStatement
版权声明:本文标题:利用JDBC的PrepareStatement打印真实SQL的方法详解 内容由林淑君副主任自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.xiehuijuan.com/baike/1687061720a130846.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论