次のメソッドシグネチャを持つ一般的なJavaメソッドがあります。
private static ResultSet runSQLResultSet(String sql, Object... queryParams)
接続を開き、sqlステートメントとPreparedStatement
可変長配列のパラメーターを使用してqueryParams
を構築し、実行し、(ResultSet
内の)CachedRowSetImpl
をキャッシュし、接続を閉じて、キャッシュされた結果セットを返します。
エラーを記録するメソッドに例外処理があります。デバッグに非常に役立つので、SQLステートメントをログの一部として記録します。私の問題は、文字列変数sql
を記録すると、実際の値ではなく?実行された(または実行しようとした)actualステートメントを記録したい。
それで... PreparedStatement
によって実行される実際のSQLステートメントを取得する方法はありますか? (Without自分でビルドします。PreparedStatement's
SQLにアクセスする方法が見つからない場合は、おそらくcatch
esで自分でビルドすることになります。)
準備されたステートメントを使用すると、「SQLクエリ」はありません。
しかし、実際のSQLクエリの再構築はありません。Java側でもデータベース側でもありません。
そのため、準備されたステートメントのSQLを取得する方法はありません-そのようなSQLは存在しないためです。
デバッグ目的での解決策は次のいずれかです。
JDBC APIコントラクトではどこにも定義されていませんが、運がよければ、問題のJDBCドライバーはPreparedStatement#toString()
を呼び出すだけで完全なSQLを返すことができます。つまり.
System.out.println(preparedStatement);
少なくともMySQL 5.xおよびPostgreSQL 8.x JDBCドライバーがサポートしています。ただし、他のほとんどのJDBCドライバーはサポートしていません。そのようなものがある場合、最善の策は Log4jdbc または P6Spy を使用することです。
または、Connection
、SQL文字列、およびステートメント値を受け取り、SQL文字列と値を記録した後に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();
// ...
別の選択肢は、構築時にrealPreparedStatement
をラップ(装飾)し、のメソッドを呼び出すようにすべてのメソッドをオーバーライドするカスタムPreparedStatement
を実装することですrealPreparedStatement
およびすべてのsetXXX()
メソッドの値を収集し、executeXXX()
メソッドの1つが呼び出されるたびに「実際の」SQL文字列を遅延的に構築します(作業を終了しますが、ほとんどのIDEにはデコレータメソッドのオートジェネレータがあります) 。最後に、代わりに使用します。それは基本的に、P6Spyと配偶者が既に内部で行っていることでもあります。
Java 8、MySQLコネクタv。5.1.31を備えたJDBCドライバーを使用しています。
このメソッドを使用して、実際のSQL文字列を取得できます。
// 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());
したがって、次のようにsmthを返します。
INSERT INTO oc_manufacturer SET manufacturer_id = 23, name = 'Google', sort_order=0;
クエリを実行し、ResultSet
を期待している場合(少なくともこのシナリオでは)、次のようにResultSet
のgetStatement()
を呼び出すことができます。
ResultSet rs = pstmt.executeQuery();
String executedQuery = rs.getStatement().toString();
変数executedQuery
には、ResultSet
の作成に使用されたステートメントが含まれます。
今、私はこの質問がかなり古いことを知っていますが、これが誰かを助けることを願っています。
PrepareStatement.toString()を使用してPreparedStatementからsqlを抽出しました。私の場合、toString()は次のようなStringを返します。
org.hsqldb.jdbc.JDBCPreparedStatement@7098b907[sql=[INSERT INTO
TABLE_NAME(COLUMN_NAME, COLUMN_NAME, COLUMN_NAME) VALUES(?, ?, ?)],
parameters=[[value], [value], [value]]]
これで、正規表現を使用してクエリと値の両方を抽出してマップに入れるメソッド(Java 8)を作成しました。
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()が私の場合と異なる場合、正規表現を「調整」するだけです。
PostgreSQL 9.6.xを公式Javaドライバー42.2.4
とともに使用:
...myPreparedStatement.execute...
myPreparedStatement.toString()
SQLを?で表示しますすでに交換されています。これは私が探していたものです。 postgresのケースをカバーするためにこの回答を追加しました。
こんなに簡単だとは思わなかったでしょう。
非常に遅い:)が、次の方法でOraclePreparedStatementWrapperから元のSQLを取得できます。
((OraclePreparedStatementWrapper) preparedStatement).getOriginalSql();
MySQLを使用している場合は、 MySQLのクエリログ を使用してクエリを記録できます。他のベンダーがこの機能を提供しているかどうかはわかりませんが、可能性はあります。
引数のリストを使用して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;
}
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[" + "] ...");
}
}