PostgreSQL™サーバでは、クライアントは再利用が想定されるSQL文を、各実行時の文の解析や計画作成のオーバーヘッドを取り除くためにコンパイルすることができます。 この機能は、サーババージョン7.3以降ではPREPAREとEXECUTEを使用してSQLレベルで、サーババージョン7.4以降ではプロトコルレベルで利用できます。 しかし、Java開発者としては、単に標準のPreparedStatementインタフェースを使用したいものです。
以前のバージョンのドライバでは、サーバ準備済み文を実装するためにPREPAREとEXECUTEを使用していました。 これは7.3以降のすべてのサーババージョンでサポートされます。 しかし、問い合わせ結果において、ResultSetメタデータが存在しない、行の更新数など、アプリケーションでわかる違いが生じました。 現在のドライバではV3プロトコルレベルの同等機能を使用しますので、問い合わせ結果における違いが生じません。 しかし、V3プロトコルはバージョン7.4以降でなければ使用できません。 7.3サーバに接続する場合や7.4サーバに明示的にV2プロトコルで接続する場合には、サーバ準備済み文を有効にしても効果はありません
サーバサイドの準備済み文を有効にする方法は、アプリケーションの必要性に応じて複数あります。 一般的な方法は、PreparedStatementの閾値を設定することです。 内部で、文が何回実行されたかを追跡するカウンタを保持しており、カウンタが閾値に達した時に、サーバサイドの準備済み文を使用するようになります。
サーバサイドの準備済み文の計画作成はサーバで一度だけ行われます。 これにより、問い合わせの計画再作成を毎回行うコストを抑えます。 しかし、これはまた、その問い合わせの特定の実行において使用される特定の値をプランナが使用することができないという欠点があることを意味します。
import java.sql.*;
public class ServerSidePreparedStatement
{
public static void main(String args[]) throws Exception
{
Class.forName("org.postgresql.Driver");
String url = "jdbc:postgresql://localhost:5432/test";
Connection conn = DriverManager.getConnection(url,"test","");
PreparedStatement pstmt = conn.prepareStatement("SELECT ?");
// PostgreSQL拡張インタフェースへキャストします。
org.postgresql.PGStatement pgstmt = (org.postgresql.PGStatement)pstmt;
// 3回目の実行で、サーバサイドの準備済み文が使用されるようになります。
pgstmt.setPrepareThreshold(3);
for (int i=1; i<=5; i++)
{
pstmt.setInt(1,i);
boolean usingServerPrepare = pgstmt.isUseServerPrepare();
ResultSet rs = pstmt.executeQuery();
rs.next();
System.out.println("Execution: "+i+", Used server side: " + usingServerPrepare + ", Result: "+rs.getInt(1));
rs.close();
}
pstmt.close();
conn.close();
}
}
これは、3回目の実行時点でサーバサイドの準備済み文が使用されるという、想定通りの結果を出力します。
Execution: 1, Used server side: false, Result: 1 Execution: 2, Used server side: false, Result: 2 Execution: 3, Used server side: true, Result: 3 Execution: 4, Used server side: true, Result: 4 Execution: 5, Used server side: true, Result: 5
上で示した例では、プログラマはPostgreSQL™固有のコードを移植性があるものと仮定できるAPI内で使用しなければならず、あまり好まれません。 また、特定の文に対してのみ閾値を設定しており、もしすべての文で閾値を指定したい場合のキー入力量が多くなってしまいます。 サーバサイドの準備済み文を有効にするために閾値を設定する他の方法を見てみましょう。 PreparedStatementの上位にすでに階層があります。 作成元となるConnectionです。 その上位に接続先となるDatasourceやURLがあります。 サーバサイドの準備済み文の閾値は、これらの階層のどこででも設定でき、そして、そのすべての下層のデフォルト値となります。
// PostgreSQL拡張インタフェース
org.postgresql.PGConnection pgconn;
org.postgresql.PGStatement pgstmt;
// このURLから作成される接続用に準備済み文の閾値を設定します。
String url = "jdbc:postgresql://localhost:5432/test?prepareThreshold=3";
// 作成された接続がurlから正しく閾値を引き継いでいるか確認します。
Connection conn = DriverManager.getConnection(url,"test","");
pgconn = (org.postgresql.PGConnection)conn;
System.out.println(pgconn.getPrepareThreshold()); // 3のはず
// 作成された文が接続から正しく閾値を引き継いでいるか確認します。
PreparedStatement pstmt = conn.prepareStatement("SELECT ?");
pgstmt = (org.postgresql.PGStatement)pstmt;
System.out.println(pgstmt.getPrepareThreshold()); // 3のはず
// 接続の閾値を変更し、新しく作成する文が引き継いでいるか確認します。
pgconn.setPrepareThreshold(5);
PreparedStatement pstmt = conn.prepareStatement("SELECT ?");
pgstmt = (org.postgresql.PGStatement)pstmt;
System.out.println(pgstmt.getPrepareThreshold()); // 5のはず