如何获取预准备语句的 SQL?

2022-08-31 06:36:32

我有一个具有以下方法签名的通用Java方法:

private static ResultSet runSQLResultSet(String sql, Object... queryParams)

它打开一个连接,使用 sql 语句和可变长度数组中的参数生成 a,运行它,缓存 (在 a 中 ),关闭连接,然后返回缓存的结果集。PreparedStatementqueryParamsResultSetCachedRowSetImpl

我在记录错误的方法中有异常处理。我将sql语句记录为日志的一部分,因为它对调试非常有帮助。我的问题是,记录 String 变量会用 ?s 而不是实际值。我想记录已执行(或尝试执行)的实际语句。sql

所以。。。有没有办法获取将由运行的实际SQL语句?(无需自己构建。如果我找不到访问SQL的方法,我最终可能会在我的es中自己构建它。PreparedStatementPreparedStatement'scatch


答案 1

使用预准备语句,没有“SQL查询”:

  • 您有一个包含占位符的语句
    • 它被发送到数据库服务器
    • 并在那里准备
    • 这意味着SQL语句被“分析”,解析,一些表示它的数据结构在内存中准备
  • 然后,你有绑定变量
    • 发送到服务器
    • 并执行预准备语句 - 处理这些数据

但是没有重新构建一个实际的实际SQL查询 - 无论是在Java端,还是在数据库端。

因此,没有办法获取预准备语句的 SQL -- 因为没有这样的 SQL。


出于调试目的,解决方案可以是:

  • 输出语句的代码,包括占位符和数据列表
  • 或者“手动”“构建”一些SQL查询。

答案 2

它在 JDBC API 合约中没有定义,但如果你很幸运,有问题的 JDBC 驱动程序可能只需调用 就返回完整的 SQL。即PreparedStatement#toString()

System.out.println(preparedStatement);

至少MySQL 5.x和PostgreSQL 8.x JDBC驱动程序支持它。但是,大多数其他 JDBC 驱动程序不支持它。如果你有这样的,那么你最好的选择是使用Log4jdbcP6Spy

或者,您也可以编写一个泛型函数,该函数采用 、 SQL 字符串和语句值,并在记录 SQL 字符串和值后返回 a。开球示例:ConnectionPreparedStatement

public static PreparedStatement prepareStatement(Connection connection, String sql, Object... values) throws SQLException {
    PreparedStatement preparedStatement = connection.prepareStatement(sql);
    for (int i = 0; i < values.length; i++) {
        preparedStatement.setObject(i + 1, values[i]);
    }
    logger.debug(sql + " " + Arrays.asList(values));
    return preparedStatement;
}

并将其用作

try {
    connection = database.getConnection();
    preparedStatement = prepareStatement(connection, SQL, values);
    resultSet = preparedStatement.executeQuery();
    // ...

另一种选择是实现一个自定义,该自定义在构造时包装(装饰)real并覆盖所有方法,以便它调用real的方法并收集所有方法中的值,并在调用其中一个方法时懒惰地构造“actual”SQL字符串(这是一项相当大的工作,但大多数IDE都为装饰器方法提供了自动生成器, Eclipse 确实如此)。最后,只需使用它即可。这基本上也是P6Spy和妃子们已经在引擎盖下做的事情。PreparedStatementPreparedStatementPreparedStatementsetXXX()executeXXX()


推荐