it-swarm.com.de

Wie kann ich die SQL einer PreparedStatement erhalten?

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 catches aufbauen.)

132
froadie

Bei der Verwendung von vorbereiteten Anweisungen gibt es keine "SQL-Abfrage":

  • Sie haben eine Anweisung, die Platzhalter enthält
    • es wird an den DB-Server gesendet
    • und dort vorbereitet
    • dies bedeutet, dass die SQL-Anweisung "analysiert" wird, analysiert wird. Einige Datenstrukturen, die sie repräsentieren, werden im Speicher vorbereitet
  • Und dann haben Sie Variablen gebunden
    • welche an den Server gesendet werden
    • und die vorbereitete Anweisung wird ausgeführt - an diesen Daten wird gearbeitet

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:

  • Ouput den Code der Anweisung mit den Platzhaltern und der Liste der Daten
  • Oder um einige SQL-Abfragen "von Hand" zu "bauen".
150
Pascal MARTIN

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.

47
BalusC

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;
23
userlond

Wenn Sie die Abfrage ausführen und eine ResultSet erwarten (Sie befinden sich zumindest in diesem Szenario), können Sie einfach ResultSets 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 ..

18
Elad Stern

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".

2
RichardK

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.

1

Sehr spät :) aber Sie können die ursprüngliche SQL von einem OraclePreparedStatementWrapper von erhalten

((OraclePreparedStatementWrapper) preparedStatement).getOriginalSql();
1
user497087

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.

0
Ionuț G. Stan

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;
        }
0
Harsh Maheswari

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[" +  "] ...");
            }

    }
0
user3349710