2009-09-03 139 views
2

我正在处理下面的代码(为了清晰起见编辑),这给了我一些Oracle中打开游标的问题。 基本上我试图从数据库中选择数据,并且返回的每一行都有0或更多行的子数据被选中并附加到记录中。 目前通过在填充主数据集以读取子数据时调用另一个函数来实现此目的。对少量的行数少于1000的情况,它可以正常工作。虽然这是用户可以使用的正常操作范围,但它们可能会请求所有行的数量可能在数千行的10个数量级。在ORA-01000上执行大容量选择结果:最大打开游标超过错误。如果我运行代码并查询v $ open_cursors,则可能会看到光标数计时,直到它结束。Java不释放oracle游标

如果我将调用子函数的行注释掉,它可以正常工作,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; 
} 
+0

您使用的是哪个版本的数据库?哪些JDBC驱动程序(Oracle的?)以及哪些版本? – APC 2009-09-03 12:50:40

+0

对不起,这里的信息有重大遗漏。 使用ojdbc14.jar的Oracle 10.2.0.1.0。 – javadeveloper 2009-09-04 08:55:35

回答

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

提示重新编写代码以解决此问题。代码运行时,光标计数现在保持良好且稳定。 感谢所有您的答案。 – javadeveloper 2009-09-04 08:17:24

0

伊克,这看起来像PowerBuilder的代码从1999年为孩子们进行多次选择是一个反模式。您需要以较少的电话拨打数据库来完成此操作...这是聊天的方式。

由于您使用的是Oracle,因此您可以尝试先使用连接,然后使用父行检索子行 - 一次完成。这是最好的解决方案。

如果您以前无法获得连接,可以将这些调用合并到一个in(id1,id2,...,idN)子句中,并以块的形式检索它们。

也可以查看结果集上的并发设置。也许你有一个可滚动的结果集?

但是你解决了它,我会担心吹虚拟机和获得OOM。您需要对搜索结果进行限制。

+0

也许你应该推荐一种方法来实现这一点。 – 2009-09-03 12:46:18

+0

由于数据的使用和呈现方式,从编码的角度来看,将数据保存在两个结果集中更容易。这两个查询都只是简单的选择,所以不会导致数据库太麻烦,并且有<50个用户,所以额外的聊天是可以容忍的。 感谢您的提示,但我并不知道通过事先连接,直到你让我看着它。 没有OOM呢,只是真的很大的结果集超时! – javadeveloper 2009-09-04 08:29:22

2

JDBC驱动程序可以在一个单一的连接去一次在具有多个结果集呛。我怀疑这会导致Oracle JDBC驱动程序出现一些错误行为(我确实已经看到它在其他问题中出现问题 - 包括关闭第一个结果集,而Oracle显然没有这样做)。我会更好地获得与标题行的连接,读取所有对象,将它们放入集合中,然后遍历它们并使用单独的结果集读取详细对象。

尽管JDBC规范没有在JDBC驱动程序上声明任何义务,但JDBC-ODBC桥每个连接显式只允许一个活动语句,所以其他JDBC驱动程序当然可以自由地有类似的限制(例如因为每个连接只有一个打开的结果集)。

0

您使用连接池吗?当您认为您关闭它们时,它可能会缓存一些PreparedStatements。

要检查您是否在这种情况下,请尝试(暂时)使用未准备好的语句或禁用连接池。