web-dev-qa-db-ja.com

単一のJDBCステートメントオブジェクトを使用して複数のクエリを実行する

これを重複としてマークする前に読んでください。私はグーグルとSOから多くを検索しましたが、探している正確な答えを見つけることができませんでした。

私の質問: JDBCでは、単一のStatementオブジェクトを使用してexecuteQuery("")を複数回呼び出すことはできますか?安全ですか? ORクエリごとにステートメントオブジェクトを閉じ、別のクエリを実行するための新しいオブジェクトを作成する必要があります。

例えば:

Connection con;
Statement s;
ResultSet rs;
ResultSet rs2;
try
{
    con = getConnection();
    s = con.prepareStatement();

    try
    {
        rs = s.executeQuery(".......................");

        // process the result set rs
    }
    finally
    {
        close(rs);
    }

    // I know what to do to rs here
    // But I am asking, should I close the Statement s here? Or can I use it again for the next query?

    try
    {
        rs2 = s.executeQuery(".......................");

        // process the result set rs2
    }
    finally
    {
        close(rs2);
    }
}
finally
{
    close(s);
    close(con);
}
27

はい、Statement(特にPreparedStatement)を再利用できます。一般的にはJDBCで再利用する必要があります。ステートメントを再利用せずに、すぐに別の同一のStatementオブジェクトを作成した場合、非効率的で不適切なスタイルになります。閉じている限り、このスニペットにいるように、finallyブロックで閉じるのが適切です。

あなたが求めているものの例については、このリンクをチェックしてください: jOOq Docs

23
Durandal

なぜあなたが尋ねているのか分かりません。 APIの設計とドキュメントは、複数のStatementexecute、およびexecuteUpdateの呼び出しにexecuteQueryオブジェクトを再利用することはまったく問題ないことを示しています。許可されない場合は、Java docに明示的に文書化されます(APIが異なる可能性が高い)。

さらに、 Statement のapidocは次のように述べています。

Statementインターフェースのすべての実行メソッドは、ステートメントの[sic]現在のResultSetオブジェクトを暗黙的に閉じます。

これは、複数回使用できることを示しています。

TL; DR:はい、以前に開いたexecuteが閉じられることがわかっている限り、単一のStatementオブジェクトでResultSetを複数回呼び出すことができます。

あなたの例ではPreparedStatementを誤って使用しているため、 execute...メソッドはStringPreparedStatement を受け入れます:

SQLException-[...]メソッドがPreparedStatementまたはCallableStatementで呼び出される場合

しかし、PreparedStatementにも答えるには:PreparedStatementの目的は、パラメータープレースホルダーを使用してステートメントをプリコンパイルし、異なるパラメーター値で複数の実行に再利用することです。

9
Mark Rotteveel

API docs には、特定のPreparedStatementインスタンスでexecuteQuery()を複数回呼び出さないことを示すものは何も見つかりません。

ただし、コードはPreparedStatementを閉じません。その場合、executeQuery()への呼び出しはSQLExceptionをスローしますが、executeQuery()によって返されるResultSetはスローします。 ResultSetは、PreparedStatementを再実行すると 自動的に閉じられます です。状況に応じて、不要になったら閉じてください。私はそれを閉じないだろう、私はそうしないことは悪いスタイルだと思うので。

[〜#〜] update [〜#〜]うーん、2つの間のコメントを逃しましたブロックを試してください。この時点でPreparedStatementを閉じた場合、SQLExceptionを取得せずにexecuteQuery()を再度呼び出すことはできません。

4
Grmpfhmbl

Prepared Statementは、クエリを記憶し、そのクエリで実行するパラメータ化された変数を受け入れる準備ができるようにデータベースに指示します。ストアドプロシージャによく似ています。

Prepared Statementは2つの主なことを実現します。

  1. クエリ変数を自動的にエスケープして、SQLインジェクションから保護します。

  2. クエリを記憶し、変数を取る準備ができるようにデータベースに指示します。

番号2は、データベースがクエリを1回解釈するだけで済み、手順を実行できる状態になるため、重要です。したがって、パフォーマンスが向上します。

実行呼び出しの間に準備済みステートメントやデータベース接続を閉じないでください。これは信じられないほど非効率的で、プロシージャを作成して記憶するように毎回データベースに指示するため、単純な古いStatementを使用するよりもオーバーヘッドが大きくなります。データベースが「ホットスポット」用に設定されていて、PreparedStatementを閉じてもクエリを覚えている場合でも、ネットワークのオーバーヘッドと短い処理時間が発生します。

要するに、ConnectionPreparedStatementは、完了するまで開いたままにしてください。

編集:実行からResultSetを返さないことについてコメントするには、これで問題ありません。 executeQueryは、実行されたクエリに対してResultSetを返します。

3
SnakeDoc

まず、私はあなたのコードについて混乱しています

s = con.prepareStatement();

Java APIにそのような関数が見つかりません。少なくとも1つのパラメーターが必要です。この関数を呼び出したい場合があります。

s = con.createStatement();

2つの操作の間に閉じずに、1つの単一のStatementインスタンスでDB2に2回アクセスするコードを実行しましたが、うまくいきます。

    String sql = "";
    String sql2 = "";
    String driver = "com.ibm.db2.jcc.DB2Driver";
    String url = "jdbc:db2://ip:port/DBNAME";
    String user = "user";
    String password = "password";
    Class.forName(driver).newInstance();
    Connection conn = DriverManager.getConnection(url, user, password);
    Statement statement = conn.createStatement();
    ResultSet resultSet = statement.executeQuery(sql);
    int count = 0;
    while (resultSet.next()) {
        count++;
    }
    System.out.println("Result row count of query number one is: " + count);
    count = 0;
    resultSet = statement.executeQuery(sql2);
    while (resultSet.next()) {
        count++;
    }
    System.out.println("Result row count of query number two is: " + count);
1
mojiayi