J'ai une méthode Java générale avec la signature de méthode suivante:
private static ResultSet runSQLResultSet(String sql, Object... queryParams)
Il ouvre une connexion, crée une PreparedStatement
à l'aide de l'instruction SQL et des paramètres du tableau queryParams
d'une longueur variable, l'exécute, met en cache la ResultSet
(dans une CachedRowSetImpl
), ferme la connexion et renvoie le jeu de résultats mis en cache.
J'ai la gestion des exceptions dans la méthode qui enregistre les erreurs. J'enregistre l'instruction SQL dans le journal car elle est très utile pour le débogage. Mon problème est que la journalisation de la variable String sql
enregistre l'instruction de modèle avec? Au lieu de valeurs réelles. Je veux enregistrer l'instruction actuelle qui a été exécutée (ou tentée de l'exécuter).
Alors ... Y a-t-il un moyen d’obtenir l’instruction SQL réelle qui sera exécutée par un PreparedStatement
? ( Sans le construisant moi-même. Si je ne trouve pas un moyen d'accéder au code SQL PreparedStatement's
, je finirai probablement par le construire moi-même dans ma catch
es.)
À l'aide d'instructions préparées, il n'y a pas de "requête SQL":
Mais il n'y a pas de reconstruction d'une vraie requête SQL, ni du côté de Java, ni du côté de la base de données.
Il n’ya donc aucun moyen d’obtenir le code SQL de l’instruction préparée, car ce type de code n'existe pas.
Pour le débogage, les solutions sont les suivantes:
Cela n'est défini nulle part dans le contrat d'API JDBC, mais si vous avez de la chance, le pilote JDBC en question peut renvoyer le code SQL complet en appelant simplement PreparedStatement#toString()
. C'est à dire.
System.out.println(preparedStatement);
Au moins, les pilotes JDBC MySQL 5.x et PostgreSQL 8.x le prennent en charge. Cependant, la plupart des autres pilotes JDBC ne le prennent pas en charge. Si vous en avez un, votre meilleur choix consiste à utiliser Log4jdbc ou P6Spy .
Sinon, vous pouvez également écrire une fonction générique qui prend une Connection
, une chaîne SQL et les valeurs de l'instruction et renvoie une PreparedStatement
après la journalisation de la chaîne SQL et des valeurs. Exemple de coup d'envoi:
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;
}
et l'utiliser comme
try {
connection = database.getConnection();
preparedStatement = prepareStatement(connection, SQL, values);
resultSet = preparedStatement.executeQuery();
// ...
Une autre alternative consiste à implémenter une PreparedStatement
personnalisée qui enveloppe (décore) le realPreparedStatement
sur la construction et remplace toutes les méthodes afin qu’elle appelle les méthodes de realPreparedStatement
et collecte les valeurs de toutes les méthodes setXXX()
et paresseusement construit la chaîne SQL "réelle" chaque fois que l'une des méthodes executeXXX()
est appelée (un travail considérable, mais la plupart des environnements de développement intégrés fournissent des générateurs automatiques pour les méthodes décoratrices, comme Eclipse le fait). Enfin, utilisez-le à la place. C'est aussi fondamentalement ce que P6Spy et ses partenaires font déjà sous le capot.
J'utilise Java 8, pilote JDBC avec le connecteur MySQL v. 5.1.31.
Je peux obtenir une vraie chaîne SQL en utilisant cette méthode:
// 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());
Donc, ça retourne comme ça:
INSERT INTO oc_manufacturer SET manufacturer_id = 23, name = 'Google', sort_order=0;
Si vous exécutez la requête et attendez un ResultSet
(vous êtes dans ce scénario, au moins), vous pouvez simplement appeler ResultSet
's getStatement()
comme suit:
ResultSet rs = pstmt.executeQuery();
String executedQuery = rs.getStatement().toString();
La variable executedQuery
contiendra l’instruction utilisée pour créer le ResultSet
.
Maintenant, je me rends compte que cette question est assez ancienne, mais j'espère que cela aidera quelqu'un ..
J'ai extrait mon sql de PreparedStatement en utilisant prepareStatement.toString () Dans mon cas, toString () renvoie String comme ceci:
org.hsqldb.jdbc.JDBCPreparedStatement@7098b907[sql=[INSERT INTO
TABLE_NAME(COLUMN_NAME, COLUMN_NAME, COLUMN_NAME) VALUES(?, ?, ?)],
parameters=[[value], [value], [value]]]
Maintenant, j'ai créé une méthode (Java 8), qui utilise regex pour extraire à la fois la requête et les valeurs et les mettre dans la carte:
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;
}
Cette méthode retourne map où nous avons des paires clé-valeur:
"query" -> "INSERT INTO TABLE_NAME(COLUMN_NAME, COLUMN_NAME, COLUMN_NAME) VALUES(?, ?, ?)"
"values" -> "value, value, value"
Maintenant, si vous voulez des valeurs sous forme de liste, vous pouvez simplement utiliser:
List<String> values = Stream.of(yourExtractedParametersMap.get("values").split(","))
.collect(Collectors.toList());
Si votre readyStatement.toString () est différent de ce qu'il est dans mon cas, il vous suffit "d'ajuster" la regex.
Utilisation de PostgreSQL 9.6.x avec le pilote Java officiel 42.2.4
:
...myPreparedStatement.execute...
myPreparedStatement.toString()
Est-ce que montrer le SQL avec le? déjà remplacé, ce que je cherchais… .. Je viens d'ajouter cette réponse pour couvrir le cas postgres.
Je n'aurais jamais pensé que cela puisse être aussi simple.
Très tard :) mais vous pouvez obtenir le code SQL d'origine d'un OraclePreparedStatementWrapper en
((OraclePreparedStatementWrapper) preparedStatement).getOriginalSql();
Si vous utilisez MySQL, vous pouvez enregistrer les requêtes à l’aide de le journal des requêtes de MySQL . Je ne sais pas si d'autres fournisseurs proposent cette fonctionnalité, mais c'est probablement le cas.
Extrait de code permettant de convertir SQL PreparedStaments avec la liste des arguments. Ça marche pour moi
/**
*
* 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;
}
J'ai implémenté le code suivant pour l'impression SQL à partir de PrepareStatement
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[" + "] ...");
}
}