20

ResultSetを閉じると自動的に閉じられるようConnectionです。しかし、を返して別のメソッドで使用したいのですが、どこでResultSet閉じるかわかりません。ConnectionPreparedStatement

public ResultSet executeQuery(String sql, String[] getValue)
{
    Connection conn = null;
    PreparedStatement pstmt = null;
    ResultSet rs = null;
    try
    {
        conn = getConn();
        pstmt = conn.prepareStatement(sql);
        if (getValue != null)
        {
            for (int i = 0; i < getValue.length; i++)
            {
                pstmt.setString(i + 1, getValue[i]);
            }
        }
        rs = pstmt.executeQuery();
    } catch (Exception e)
    {
        e.printStackTrace();
        closeAll(conn, pstmt, rs);
    }
    return rs;
}

キャッチブロックに移動closeAll(conn, pstmt, null);したのは、最後にブロックに入れると、rs戻る直前に失くしてしまうことがわかったためです。これで、を閉じたいときに、とを閉じるrsことができません。解決策はありますか?connpstmt

4

10 に答える 10

33

CachedRowSet切断後の情報保持に使用

Connection con = ...
ResultSet rs = ...

CachedRowSet rowset = new CachedRowSetImpl();
rowset.populate(rs);

con.close()
于 2009-12-15T20:14:53.380 に答える
20

これをきれいにコーディングする方法の 1 つは、結果セットを取得するコールバック メソッドを持つオブジェクトを渡すことです。

もう 1 つのメソッドは、resultSet 処理コードを含むコールバック メソッドでオブジェクトを作成し、それを SQL を実行するメソッドに渡します。

そうすれば、SQL と DB コードは本来の場所にとどまり、結果セット処理ロジックはデータを使用する場所に近くなり、SQL コードは必要なときにクリーンアップされます。

  interface ResultSetCallBack{
    void handleResultSet(ResultSet r);
  }

  void executeQuery(..., ResultSetCallBack cb){
    //get resultSet r ...
    cb.handleResultSet(r);
    //close connection
  }

  void printReport(){
    executeQuery(..., new ResultSetCallBack(){
      public void handleResultSet(ResultSet r) {
        //do stuff with r here
      }
    });
  }
于 2009-12-15T20:14:28.327 に答える
5

リソース リークを避けるために、メソッド ブロックが取得され閉じられるメソッド ブロックの外部に(またはまたは) 絶対に渡さないでください。一般的な方法は、対象のデータを表す単なる JavaBean オブジェクトである に をマッピングすることです。ResultSetStatementConnectionResultSetList<Data>Data

基本的な例を次に示します。

public class Data {
    private Long id;
    private String name;
    private Integer value;
    // Add/generate public getters + setters.
}

これを正しく処理する方法の基本的な例を次に示します。

public List<Data> list() throws SQLException {
    Connection connection = null;
    PreparedStatement statement = null;
    ResultSet resultSet = null;
    List<Data> list = new ArrayList<Data>();

    try {
        connection = database.getConnection();
        statement = connection.prepareStatement("SELECT id, name, value FROM data");
        resultSet = statement.executeQuery();
        while (resultSet.next()) {
            Data data = new Data();
            data.setId(resultSet.getLong("id"));
            data.setName(resultSet.getString("name"));
            data.setValue(resultSet.getInt("value"));
            list.add(data);
        }
    } finally {
        if (resultSet != null) try { resultSet.close(); } catch (SQLException logOrIgnore) {}
        if (statement != null) try { statement.close(); } catch (SQLException logOrIgnore) {}
        if (connection != null) try { connection.close(); } catch (SQLException logOrIgnore) {}
    }

    return list;
}

次のように使用できます。

List<Data> list = dataDAO.list();

JDBC のベスト プラクティスの詳細については、この基本的なキックオフ記事も役立つ場合があります。

于 2009-12-15T20:38:05.753 に答える
3

現在の方法では、接続が閉じられることはなく、プログラムと RDBMS で後で (すぐではないにしても) 問題が発生します。ResultSet からフィールドを保持し、それを返す Java クラスを作成することをお勧めします。ResultSet は接続にリンクされているため、それを返して接続を閉じることはできません。

于 2009-12-15T20:13:40.157 に答える
3

および/またはResultSetを閉じた後は使用できません。したがって、コールバックを行うオブジェクトをこのメソッドに渡す必要があります。ConnectionPreparedStatement

すべてのクリーンアップはfinallyブロック単位で行う必要があります。

このように書き直します

public ResultSet executeQuery(
    String sql,
    String[] getValue,
    CallbackObj cbObj
  ) throws SQLException
{
  final Connection conn = getConn( );

  try
  {
    final PreparedStatement pstmt = conn.prepareStatement(sql);

    try
    {
      if (getValue != null)
      {
        for (int i = 0; i < getValue.length; i++)
        {
          pstmt.setString(i + 1, getValue[i]);
        }
      }

      final ResultSet rs = pstmt.executeQuery();

      try
      {
        cbObj.processResultSet( rs );
      }
      finally
      {
        // You may want to handle SQLException
        // declared by close
        rs.close( );
      }
    }
    finally
    {
      // You may want to handle SQLException
      // declared by close
      pstmt.close( );
    }
  }
  finally
  {
    // You may want to handle SQLException
    // declared by close
    conn.close( );
  }
}
于 2009-12-15T20:22:09.547 に答える
3

を呼び出しResultSet.getStatementて を取得しStatementたり、Statement.getConnectionを取得したりできますConnection

これらから、closeResultSet3 つすべてを閉じるユーティリティ メソッドを作成できますResultSet

于 2009-12-15T21:34:40.293 に答える
3

ResultSet を返したいときに JDBC 接続を閉じる場所

実際、あなたはほとんどその質問に自分で答えています。実験したように、 を閉じると、Connection関連付けられている JDBC リソースが解放されます (少なくとも、これが動作するはずです)。したがって、a を返したい場合ResultSet(これについては後で説明します)、「後で」接続を閉じる必要があります。これを行う 1 つの方法は、次のようにメソッドに接続を渡すことです。

public ResultSet executeQuery(Connection conn, String sql, String[] getValue);

問題は、あなたの最終的な目標が何であるか、なぜそんなに低レベルのものが必要なのか、私にはよくわからないことです。そのため、これが良いアドバイスかどうかはわかりません. 低レベルの JDBC フレームワークを作成している場合 (および、これを行っていないとは言わないでください) でない限り、. を返すことは実際にはお勧めしませんResultSet。たとえば、あるビジネス クラスにフィードしたい場合は、JDBC に依存しないオブジェクトまたはそれらのコレクションを、ResultSet. また、 aaであるため、 a を使用しない場合は a を使用しないでください。RowSet ResultSetResultSetRowSet

個人的には、車輪を再発明するのではなく、何らかのヘルパー クラスを使用する必要があると思います。Spring はやり過ぎかもしれず、学習曲線が少しありますが (まったく知らない場合は多すぎます)、Spring が唯一の方法ではありません。Commons DbUtilsを確認することを強くお勧めします。より具体的にはQueryRunner、特に次のquery()方法を見てください。

public <T> T query(String sql,
                   ResultSetHandler<T> rsh,
                   Object... params)
        throws SQLException

ご覧のとおり、このメソッドは、 z5h の回答で説明されているように、他のオブジェクトResultSetHandlerに変換するコールバック メソッドを公開する aを渡すことができ、DbUtils はいくつかの実装を提供します。クラスのユーティリティ メソッドも参照してください。たとえば、JDBC リソースを閉じるのに便利なさまざまなメソッドがあります。ResultSetsDbUtilsDbUnit.close()

本当に、そうする非常に正当な理由がない限り (そして、私はそれらを知りたいと思います)、さらに別の JDBC フレームワークを作成しないでください。実証済みの優れた設計の恩恵を受けることができます。低レベルのものであっても、これまで見てきたように既存の (そして単純な) ソリューションがあります。少なくとも、それをチェックしてください。

于 2009-12-16T16:42:27.090 に答える
1

次のようなことをすることをお勧めします。

public List<Map> executeQuery(Connection connection, String sql) throws SQLException
{
    List<Map> rows = new ArrayList<Map>();

    PreparedStatement stmt = null;
    ResultSet rs = null;

    try
    {
        pstmt = conn.prepareStatement(sql);
        rs = stmt.execute();
        int numColumns = rs.getMetaData().getColumnCount();

        while (rs.next())
        {
            Map<String, Object> row = new LinkedHashMap<String, Object>();
            for (int i = 0; i < numColumns; ++i)
            {
                String column = rs.getColumnName(i+1);
                Object value = rs.getObject(i+1);
                row.put(column, value);
            }
            rows.add(row);
        }
    } 
    finally
    {
        close(rs);
        close(stmt);
    }

    return rows;
}

public static void close(Statement s)
{
    try
    {
        if (s != null)
        {
            s.close();
        }
    }
    catch (SQLException e)
    {
        e.printStackTrace();
    }
}

public static void close(ResultSet rs)
{
    try
    {
        if (rs != null)
        {
            rs.close();
        }
    }
    catch (SQLException e)
    {
        e.printStackTrace();
    }
}
于 2009-12-15T23:26:52.383 に答える
1

下位レベルで JDBC を扱うべきではありません。代わりに、 Springのようなフレームワークを使用してください。必要なすべてのclose()操作が処理されます。

于 2009-12-15T20:15:53.673 に答える