Java does not release oracle cursors

I am working on the following code below (edited for clarity) which gives me several problems with open cursors in Oracle. Basically I am trying to fetch data from DB and for each returned row, there are 0 or more rows of ancillary data that need to be fetched and appended to the record. This is currently achieved by calling another function when populating the main dataset to read auxiliary data. Which is great for small amounts of lines, less than 1000. While this is a normal operating range that users will probably use, they will probably ask for all lines, which could be on the order of 10 thousand lines. High volume execution fetches results in ORA-01000: maximum open cursors exceed error. If I run the code and query v $ open_cursors, you can see the cursor count untiluntil he falls.

If I comment out the line calling the subfunction, it works fine, the number of cursors in v $ open_cursors just hovers up and down by a few counts.

I noticed that the main function is to pass the connection object through a subfunction, and I thought that this could cause the resulting statements and target objects to remain open while the connection is still open, even if closed by code. So I tried to change the code so that each function would get its own connection from the pool and close it when it was done, but that didn't make any difference for cursors.

I could increase the number of cursors, but a) that just masks the problem, b) I have to stick to it to a stupidly large number to make it work, and c) I don't want to release flawed code!

However, I was running out of ideas on how to get the code to release cursors.

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;
} 

      

+2


source to share


4 answers


You can try to pre-prepare both the main ("leading") and auxiliary ("detailed") statements:



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();
    }
}

      

+1


source


JDBC drivers can be choked with multiple result sets on the same connection. I suspect this is leading to misbehavior regarding the Oracle JDBC driver (I have certainly seen it cause problems in others, including closing the first result set on you, which Oracle obviously doesn't). I would be much better off getting a connection to the title lines, read all of your objects, put them in a collection, and then iterate through them and read the details with separate result sets.



While the JDBC specification does not indicate any commitment to the JDBC driver with respect to this, the JDBC-ODBC bridge explicitly only allows one live statement per connection, so other JDBC drivers can certainly have similar restrictions (for example, only one open result set for each connection).

+2


source


Eek, this looks like PowerBuilder code from 1999. Making multiple selections for children is an anti-pattern. You need to do it in fewer DB calls ... this is the way to communicate.

Since you are in Oracle, you can try to use a join before getting child rows with parent rows - all at once. This is the best solution.

If you can't get the connection still, you can bundle the calls into an in (id1, id2, ..., idN) clause and get them in chunks.

Also you can look at the concurrency settings on the result set. Maybe you have a scrollable result set?

However you solve this, I will worry about blowing the VM and getting the OOM. For search results, you need a string constraint.

0


source


Are you using connection pooling? It could be caching some PreparedStatements when you think you are closing them.

To make sure you are in this case, try (temporarily) using unprepared statements or disable the connection pool.

0


source







All Articles