我如何获得PreparedStatement的SQL?

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

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

它打开一个连接,使用sql语句和queryParams可变长度数组中的参数构buildPreparedStatement ,运行它,cachingResultSet (在CachedRowSetImpl ),closures连接并返回caching的结果集。

我在logging错误的方法中有exception处理。 我将sql语句logging为日志的一部分,因为它对debugging非常有帮助。 我的问题是,日志logging的stringvariablessql日志的模板语句?而不是实际值。 我想logging执行(或试图执行)的实际语句。

所以…有什么办法来获得将由PreparedStatement运行的实际SQL语句? (如果我自己没有构build它,如果我找不到一个访问PreparedStatement's SQL的方法,那么我可能最终会自己去构build它。)

使用预处理语句,不存在“SQL查询”:

  • 您有一个包含占位符的声明
    • 它被发送到数据库服务器
    • 并在那里准备
    • 这意味着SQL语句被“分析”,parsing,代表它的一些数据结构被准备在内存中
  • 然后,你有约束variables
    • 哪些被发送到服务器
    • 准备好的陈述被执行 – 处理这些数据

但是没有重构一个实际的真正的SQL查询 – 无论是在Java方面,还是在数据库方面。

所以,没有办法获得准备好的语句的SQL – 因为没有这样的SQL。

为了debugging目的,解决scheme要么是:

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

在JDBC API合同中没有定义,但如果幸运的话,那么所讨论的JDBC驱动程序就可以通过调用PreparedStatement#toString()来返回完整的SQL。 即

 System.out.println(preparedStatement); 

至lessMySQL 5.x和PostgreSQL 8.x JDBC驱动程序支持它。 但是,大多数其他JDBC驱动程序不支持它。 如果你有这样一个,那么你最好的select是使用Log4jdbc或P6Spy 。

或者,您也可以编写一个通用函数,该函数接收一个Connection ,一个SQLstring和语句值,并在loggingSQLstring和值之后返回一个PreparedStatement 。 开球的例子:

 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(); // ... 

另一种方法是实现一个自定义的PreparedStatement ,它在构造时包装(装饰) 真实的 PreparedStatement ,并覆盖所有的方法,以便调用真正的 PreparedStatement的方法,并收集所有setXXX()方法中的值,并且懒惰地构造“actual “每当一个executeXXX()方法被调用时,SQLstring(相当一部分工作,但是大多数IDE为decorator方法提供了自动生成器,Eclipse就是这样做的)。 最后只是用它来代替。 这也基本上是P6Spy和配合已经在引擎盖下做的。

如果您正在执行查询并期待ResultSet (至less在这种情况下),那么您可以简单地调用ResultSetgetStatement()如下所示:

 ResultSet rs = pstmt.executeQuery(); String executedQuery = rs.getStatement().toString(); 

executableQueryvariables将包含用于创buildResultSet的语句。

现在,我意识到这个问题是相当古老的,但我希望这可以帮助一个人..

我正在使用Java 8,JDBC驱动程序和MySQL连接器v 5.1.31。

我可能会得到真正的SQLstring使用这种方法:

 // 1. make connection somehow, it's conn variable // 2. make prepered statement template PreparedStatement stmt = conn.prepareStatement( "INSERT INTO oc_manufacturer" + " SET" + " manufacturer_id = ?," + " name = ?," + " sort_order=0;" ); // 3. fill template stmt.setInt(1, 23); stmt.setString(2, 'Google'); // 4. print sql string System.out.println(((JDBC4PreparedStatement)stmt).asSql()); 

所以它返回水平如下:

 INSERT INTO oc_manufacturer SET manufacturer_id = 23, name = 'Google', sort_order=0; 

如果您使用MySQL,则可以使用MySQL的查询日志logging查询 。 我不知道其他厂商是否提供这个function,但他们有机会。

简单的function:

 public static String getSQL (Statement stmt){ String tempSQL = stmt.toString(); //please cut everything before sql from statement //javadb...: int i1 = tempSQL.indexOf(":")+2; tempSQL = tempSQL.substring(i1); return tempSQL; } 

准备好的语句也很好。

我正在使用Oralce 11g,无法从PreparedStatement获取最终的SQL。 在阅读@帕斯卡尔马丁答案我明白为什么。

我放弃了使用PreparedStatement的想法,并使用了适合我需求的简单文本格式器。 这是我的例子:

 //I jump to the point after connexion has been made ... java.sql.Statement stmt = cnx.createStatement(); String sqlTemplate = "SELECT * FROM Users WHERE Id IN ({0})"; String sqlInParam = "21,34,3434,32"; //some random ids String sqlFinalSql = java.text.MesssageFormat(sqlTemplate,sqlInParam); System.out.println("SQL : " + sqlFinalSql); rsRes = stmt.executeQuery(sqlFinalSql); 

你知道sqlInParam可以在(for,while)循环中dynamic构build,我只是简单地使用MessageFormat类作为SQL查询的string模板formater。

我实现了从PrepareStatement打印SQL的以下代码

 public void printSqlStatement(PreparedStatement preparedStatement, String sql) throws SQLException{ String[] sqlArrya= new String[preparedStatement.getParameterMetaData().getParameterCount()]; try { Pattern pattern = Pattern.compile("\\?"); Matcher matcher = pattern.matcher(sql); StringBuffer sb = new StringBuffer(); int indx = 1; // Parameter begin with index 1 while (matcher.find()) { matcher.appendReplacement(sb,String.valueOf(sqlArrya[indx])); } matcher.appendTail(sb); System.out.println("Executing Query [" + sb.toString() + "] with Database[" + "] ..."); } catch (Exception ex) { System.out.println("Executing Query [" + sql + "] with Database[" + "] ..."); } } 

代码片段将SQL PreparedStaments与参数列表进行转换。 它适用于我

  /** * * formatQuery Utility function which will convert SQL * * @param sql * @param arguments * @return */ public static String formatQuery(final String sql, Object... arguments) { if (arguments != null && arguments.length <= 0) { return sql; } String query = sql; int count = 0; while (query.matches("(.*)\\?(.*)")) { query = query.replaceFirst("\\?", "{" + count + "}"); count++; } String formatedString = java.text.MessageFormat.format(query, arguments); return formatedString; } 

我已经从PreparedStatement使用preparedStatement.toString()提取我的SQL在我的情况toString()返回string是这样的:

 org.hsqldb.jdbc.JDBCPreparedStatement@7098b907[sql=[INSERT INTO TABLE_NAME(COLUMN_NAME, COLUMN_NAME, COLUMN_NAME) VALUES(?, ?, ?)], parameters=[[value], [value], [value]]] 

现在我创build了一个方法(Java 8),它使用正则expression式来提取查询和值,并将它们放到地图中:

 private Map<String, String> extractSql(PreparedStatement preparedStatement) { Map<String, String> extractedParameters = new HashMap<>(); Pattern pattern = Pattern.compile(".*\\[sql=\\[(.*)],\\sparameters=\\[(.*)]].*"); Matcher matcher = pattern.matcher(preparedStatement.toString()); while (matcher.find()) { extractedParameters.put("query", matcher.group(1)); extractedParameters.put("values", Stream.of(matcher.group(2).split(",")) .map(line -> line.replaceAll("(\\[|])", "")) .collect(Collectors.joining(", "))); } return extractedParameters; } 

这个方法返回我们有键值对的地图:

 "query" -> "INSERT INTO TABLE_NAME(COLUMN_NAME, COLUMN_NAME, COLUMN_NAME) VALUES(?, ?, ?)" "values" -> "value, value, value" 

现在 – 如果你想把值作为列表,你可以简单地使用:

 List<String> values = Stream.of(yourExtractedParametersMap.get("values").split(",")) .collect(Collectors.toList()); 

如果您的preparedStatement.toString()是不同于我的情况,这只是一个“调整”正则expression式的问题。

要做到这一点,你需要一个JDBC连接和/或驱动程序,支持在较低的水平loginSQL。

看看log4jdbc