Ich habe eine allgemeine Java-Methode mit der folgenden Methodensignatur:
private static ResultSet runSQLResultSet(String sql, Object... queryParams)
Es öffnet eine Verbindung, erstellt eine PreparedStatement
mit der sql-Anweisung und den Parametern im Array queryParams
mit variabler Länge, führt sie aus, speichert die ResultSet
(in einer CachedRowSetImpl
), schließt die Verbindung und gibt die zwischengespeicherte Ergebnismenge zurück.
Ich habe eine Ausnahmebehandlung in der Methode, die Fehler protokolliert. Ich protokolliere die SQL-Anweisung als Teil des Protokolls, da dies beim Debuggen sehr hilfreich ist. Mein Problem ist, dass das Protokollieren der String-Variablen sql
die Vorlagenanweisung mit? S anstelle von tatsächlichen Werten protokolliert. Ich möchte die actual -Anweisung protokollieren, die ausgeführt wurde (oder versucht wurde, sie auszuführen).
Gibt es eine Möglichkeit, die eigentliche SQL-Anweisung abzurufen, die von einer PreparedStatement
ausgeführt wird? ( Ohne Ich baue es selbst. Wenn ich keinen Zugriff auf die PreparedStatement's
-SQL finden kann, werde ich es wahrscheinlich selbst in meinen catch
es aufbauen.)
Bei der Verwendung von vorbereiteten Anweisungen gibt es keine "SQL-Abfrage":
Es gibt jedoch keine Neuerstellung einer echten SQL-Abfrage - weder auf Java- noch auf Datenbankseite.
Es gibt also keine Möglichkeit, die SQL der vorbereiteten Anweisung abzurufen - da es keine solche SQL gibt.
Zum Zweck der Fehlersuche sind die Lösungen entweder:
Es ist nirgendwo im JDBC-API-Vertrag definiert, aber wenn Sie Glück haben, gibt der betreffende JDBC-Treiber möglicherweise die vollständige SQL zurück, indem Sie einfach PreparedStatement#toString()
aufrufen. D.h.
System.out.println(preparedStatement);
Zumindest die JDBC-Treiber von MySQL 5.x und PostgreSQL 8.x unterstützen dies. Die meisten anderen JDBC-Treiber unterstützen es jedoch nicht. Wenn Sie einen solchen haben, ist Ihre beste Wette die Verwendung von Log4jdbc oder P6Spy .
Alternativ können Sie auch eine generische Funktion schreiben, die eine Connection
, eine SQL-Zeichenfolge und die Anweisungswerte übernimmt und nach der Protokollierung der SQL-Zeichenfolge und der Werte eine PreparedStatement
zurückgibt. Kickoff-Beispiel:
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;
}
und benutze es als
try {
connection = database.getConnection();
preparedStatement = prepareStatement(connection, SQL, values);
resultSet = preparedStatement.executeQuery();
// ...
Eine andere Alternative ist die Implementierung einer benutzerdefinierten PreparedStatement
, die die realPreparedStatement
bei der Konstruktion umgibt (dekoriert) und alle Methoden überschreibt, sodass die Methoden der realPreparedStatement
aufgerufen werden und die Werte in allen setXXX()
-Methoden erfasst werden und konstruiert faul die "tatsächliche" SQL-Zeichenfolge, wenn eine der executeXXX()
-Methoden aufgerufen wird (eine ziemlich gute Arbeit, aber die meisten IDEs bieten Autogeneratoren für Dekorationsmethoden an, Eclipse tut dies). Zum Schluss benutzen Sie es einfach stattdessen. Das ist im Grunde auch das, was P6Spy und Konsorten bereits unter den Hauben tun.
Ich verwende Java 8, JDBC-Treiber mit MySQL Connector Version 5.1.31.
Ich kann echte SQL-Zeichenfolge mit dieser Methode erhalten:
// 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());
So gibt es so zurück:
INSERT INTO oc_manufacturer SET manufacturer_id = 23, name = 'Google', sort_order=0;
Wenn Sie die Abfrage ausführen und eine ResultSet
erwarten (Sie befinden sich zumindest in diesem Szenario), können Sie einfach ResultSet
s getStatement()
wie folgt aufrufen:
ResultSet rs = pstmt.executeQuery();
String executedQuery = rs.getStatement().toString();
Die Variable executedQuery
enthält die Anweisung, mit der die ResultSet
erstellt wurde.
Jetzt ist mir klar, dass diese Frage ziemlich alt ist, aber ich hoffe, das hilft jemandem ..
Ich habe mein SQL aus PreparedStatement extrahiert, indem Sie readyStatement.toString () in meinem Fall toString () zurückgibt:
[email protected][sql=[INSERT INTO
TABLE_NAME(COLUMN_NAME, COLUMN_NAME, COLUMN_NAME) VALUES(?, ?, ?)],
parameters=[[value], [value], [value]]]
Jetzt habe ich eine Methode (Java 8) erstellt, die regex verwendet, um sowohl die Abfrage als auch die Werte zu extrahieren und in die Map einzufügen:
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;
}
Diese Methode gibt eine Map zurück, bei der wir Schlüssel-Wert-Paare haben:
"query" -> "INSERT INTO TABLE_NAME(COLUMN_NAME, COLUMN_NAME, COLUMN_NAME) VALUES(?, ?, ?)"
"values" -> "value, value, value"
Wenn Sie Werte als Liste wünschen, können Sie einfach Folgendes verwenden:
List<String> values = Stream.of(yourExtractedParametersMap.get("values").split(","))
.collect(Collectors.toList());
Wenn Ihr PrepareStatement.toString () anders ist als in meinem Fall, dann ist es nur eine Frage des Regens "anpassen".
PostgreSQL 9.6.x mit dem offiziellen Java-Treiber verwenden 42.2.4
:
...myPreparedStatement.execute...
myPreparedStatement.toString()
Zeigt die SQL mit der? bereits ersetzt, was ich suchte ... __ fügte nur diese Antwort hinzu, um den Postgres-Fall zu behandeln.
Ich hätte nie gedacht, dass es so einfach sein könnte.
Sehr spät :) aber Sie können die ursprüngliche SQL von einem OraclePreparedStatementWrapper von erhalten
((OraclePreparedStatementWrapper) preparedStatement).getOriginalSql();
Wenn Sie MySQL verwenden, können Sie die Abfragen mit MySQLs Abfrageprotokoll protokollieren. Ich weiß nicht, ob andere Anbieter diese Funktion anbieten, aber die Chancen stehen gut.
Code-Snippet zum Konvertieren von SQL PreparedStaments mit der Liste der Argumente. Für mich geht das
/**
*
* 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;
}
Ich habe den folgenden Code zum Drucken von SQL aus PrepareStatement implementiert
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[" + "] ...");
}
}