2

以下のコードに取り組んでいます (わかりやすくするために編集しています)。これにより、Oracle で開いているカーソルに関するいくつかの問題が発生しています。基本的に、DB からデータを選択しようとしています。返された行ごとに、選択してレコードに追加するサブデータの行が 0 行以上あります。これは現在、サブ データを読み取るためにメイン データ セットにデータを入力しながら、別の関数を呼び出すことによって実現されています。これは、1000 未満の少量の行では問題なく機能します。これは、ユーザーが使用する通常の動作範囲ですが、数万行のオーダーになる可能性があるすべての行を要求する可能性があります。大規模なボリューム選択を実行すると、ORA-01000: 開いているカーソルの最大数を超えましたエラー。コードを実行して v$open_cursors にクエリを実行すると、カーソルが落ちるまでカウントアップしていることがわかります。

sub 関数を呼び出している行をコメントアウトすると、正常に動作し、v$open_cursors のカーソル数が数カウントだけ上下に変動します。

メイン関数がその接続オブジェクトをサブ関数に渡していることに気付きました。これにより、結果のステートメントと結果セットが、コードによって閉じられているにもかかわらず、接続がまだ開いている間に開いたままになる可能性があると考えました。そのため、各関数がプールから独自の接続を取得し、完了したら閉じるようにコードを変更しようとしましたが、カーソルに違いはありませんでした。

カーソルの数を増やすことはできますが、a) 問題を覆い隠すだけであり、b) 動作させるにはばかげたほど多くのカーソルを保持する必要があり、c) 欠陥のあるコードをリリースしたくありません!

ただし、コードでカーソルを解放する方法についてのアイデアが不足しています。

public ArrayList getCustomerSearchResult(Connection con) throws AnException {
    ResultSet rst = null;
    PreparedStatement stmt = null;
    ArrayList resultList = new ArrayList();

    String sql = "----  The search SQL string --- ";

    try {
        stmt = con.prepareStatement(sql);
        rst = stmt.executeQuery();

        while(rst.next()) {
            DataDTO data = new DataDTO();

            data.setSomeData(rst.getString("...."));

            // ##### This call is where the problem lies #####
            data.setSomeSubDataAsAnArrayList(getSubDataForThisRow(data.getId(), con));

            resultList.add(data);
        }

    } catch(Exception e) {
        throw new AnException("Error doing stuff", e);
    } finally{
        try{
          rst.close();
          stmt.close();
          rst = null;
          stmt = null;
        }catch(Exception ex){
            throw new AnException("Error doing stuff", ex);
        }
    }
    return resultList;
}

public ArrayList getSubDataForThisRow(String Id, Connection con) throws AnException {
    ResultSet rst = null;
    PreparedStatement stmt = null;
    ArrayList resultList = new ArrayList();

    String sql = "----  The search SQL string --- ";

    try {
        stmt = con.prepareStatement(sql);
        stmt.setString(1, Id);
        rst = stmt.executeQuery();

        while(rst.next()) {
            SubDataDTO data = new SubDataDTO();

            data.setSomeData(rst.getString("...."));

            resultList.add(data);
        }

    } catch(Exception e) {
        throw new AnException("Error!", e);
    } finally{
        try{
            rst.close();
            stmt.close();
            rst = null;
            stmt = null;
          }catch(Exception ex){
              throw new AnException("Error!", ex);
          }
      }

    return resultList;
} 
4

4 に答える 4

2

JDBCドライバーは、単一の接続で複数の結果セットを同時に実行することを妨げる可能性があります。これがOracleのJDBCドライバーでバグのある動作を引き起こしているのではないかと思います(Oracleが明らかに行っていない最初の結果セットを閉じるだけで、他のドライバーで問題が発生することは確かです)。ヘッダー行への接続を取得し、すべてのオブジェクトを読み取り、それらをコレクションに入れてから、それらを繰り返し処理して、個別の結果セットを持つ詳細オブジェクトを読み取る方がはるかに良いでしょう。

JDBC仕様には、これに関するJDBCドライバーに対する義務は記載されていませんが、JDBC-ODBCブリッジでは、接続ごとに1つのアクティブステートメントのみが明示的に許可されているため、他のJDBCドライバーにも同様の制限があります(1つだけ開くなど)。接続ごとの結果セット)。

于 2009-09-03T13:37:08.753 に答える
1

メイン (「マスター」) とサブ (「詳細」) ステートメントの両方を事前に準備してみてください。

PreparedStatement masterStatement = masterConnection.prepareStatement("...");
PreparedStatement detailStatement = detailConnection.prepareStatement("SELECT ... WHERE something = ?");


ResultSet masterResults = masterStatement.executeQuery();
while (masterResults.next()) {
    detailStatement.setInt(1, ...);

    ResultSet detailResults = detailStatement.executeQuery();
    try {
        while (detailResults.next()) {
        }
    } finally {
        detailResults.close();
    }
}
于 2009-09-03T12:45:02.977 に答える
0

ええ、これは 1999 年の PowerBuilder コードのようです。子に対して複数の選択を実行することはアンチパターンです。DBへの呼び出しを少なくする必要があります...それはおしゃべりです。

Oracle を使用しているため、子行を親行と一緒に一度に取得する前に、connect by を使用することができます。それが最善の解決策です。

事前に接続を取得できない場合は、呼び出しを in(id1,id2,...,idN) 句に結合して、チャンクで取得できます。

また、結果セットの同時実行設定を確認してください。たぶん、スクロール可能な結果セットがありますか?

どのように解決しても、VM を吹き飛ばして OOM になるのではないかと心配です。検索結果には行制限が必要です。

于 2009-09-03T12:44:39.853 に答える
0

接続プールを使用していますか? あなたがそれらを閉じたと思うとき、いくつかの PreparedStatements をキャッシュしているかもしれません。

この場合かどうかを確認するには、準備されていないステートメントを (一時的に) 使用するか、接続プールを無効にしてみてください。

于 2009-09-03T18:02:37.520 に答える