Java
MySQL
PostgreSQL
JDBC

JDBCでPreparedStatementを利用する際、実際に発行するクエリ文字列を取得する方法

はじめに

 PreparedStatementを利用する際、プレースホルダに値をバインドした後の実際に発行されるクエリ文字列を知りたい場合があります(ログに出力する等)。フレームワークを利用する場合は、フレームワークから発行クエリ履歴を取得できたりしますが、素のJDBCを利用する際の取得方法をあまり見ないので書いてみました。
 なお、詳細はJDBCドライバにより異なるので、以下ではMySQLPostgreSQLについて記載します。

環境

プロダクト名 バージョン
1 Java 8
2 MySQL JDBC Driver 5.1.44
3 PostgreSQL JDBC Driver 42.1.4

サンプルコード

 実際にプレースホルダ置換後のクエリ文字列を取得・表示するサンプルです。ローカル環境のデータベース[sample]、テーブル[ACCOUNT]のカラム[name]にアクセスする際のクエリ文字列を取得・表示します。

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * PreparedStatementからプレースホルダ置換後のクエリ文字列を取得するサンプル
 */
public class JdbcTest {

    public static void main(String... args) {
        useMySQL();
        System.out.println("=====================");
        usePostgres();
    }

    /**
     * MySQL版
     */
    private static void useMySQL() {
        try (Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1/sample", "ユーザー名", "パスワード");
                PreparedStatement statement = connection.prepareStatement( "select * from ACCOUNT where (name like ?)");) {

            statement.setString(1, "%hoge%");
            // ここでクエリ文字列を取得・表示
            System.out.println("mysql    > " + statement.toString());

            if (statement instanceof com.mysql.jdbc.PreparedStatement) {
                // MySQL版PreparedStatementにキャストして、MySQL固有の機能を利用できるようにする
                com.mysql.jdbc.PreparedStatement mysqlStatement = (com.mysql.jdbc.PreparedStatement) statement;
                System.out.println("参照無し版 > " + mysqlStatement.asSql());
            }
        } catch (SQLException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    /**
     * PostgreSQL版
     */
    private static void usePostgres() {
        try (Connection connection = DriverManager.getConnection("jdbc:postgresql://127.0.0.1:5432/sample", "ユーザー名", "パスワード");
                PreparedStatement statement = connection.prepareStatement("select * from ACCOUNT where name = ?");) {

            statement.setString(1, "hogehoge");
            // ここでクエリ文字列を取得・表示
            System.out.println("postgres     > " + statement.toString());

            if (statement instanceof org.postgresql.jdbc.PgStatement) {
                // PostgreSQL版PreparedStatementには、asSqlメソッドは存在しない
                org.postgresql.jdbc.PgStatement postgresStatement = (org.postgresql.jdbc.PgStatement) statement;
                System.out.println("こちらは変わらず > " + postgresStatement.toString());
            }
        } catch (SQLException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }
}

出力

mysql    > com.mysql.jdbc.JDBC42PreparedStatement@5ef04b5: select * from ACCOUNT where (name like '%hoge%')
参照無し版 > select * from ACCOUNT where (name like '%hoge%')
=====================
postgres     > select * from ACCOUNT where name = 'hogehoge'
こちらは変わらず > select * from ACCOUNT where name = 'hogehoge'

まとめ

 サンプルコードを見れば分かる通り、基本的にjava.util.PreparedStatement#toString()を実行することで、プレースホルダ置換後のクエリ文字列を取得できます。ただし、MySQLの場合は、取得できる文字列に参照情報も含まれる為、純粋にクエリ文字列だけ欲しい場合はcom.mysql.jdbc.PreparedStatement#asSql()を実行する必要があります。