Java不发布oracle游标
我正在编写下面的代码(为了清晰起见进行了编辑),这给了我一些关于Oracle中开放游标的问题。 基本上,我试图从数据库中选择数据,对于返回的每一行,都有0行或多行子数据要选择并附加到记录中。 目前,这是通过调用另一个函数来实现的,同时填充主数据集以读取子数据。它适用于较小的行数(小于1000行)。虽然这是用户将使用的正常操作范围,但他们可能会请求所有行,这些行的顺序可能是10行或数千行。执行大容量选择会导致ORA-01000:超出最大打开游标错误。如果我运行代码并查询v$open\u游标,则可能会看到游标计数一直计时到结束 如果我注释掉调用子函数的行,它工作正常,那么v$open_cursors中的游标计数只是上下波动几个计数 我注意到主函数正在将其连接对象传递给子函数,并认为这可能会导致结果语句和结果集在连接仍然打开时保持打开状态,即使它们已被代码关闭。因此,我尝试更改代码,使每个函数从池中获得自己的连接,并在完成时关闭它,但这对游标没有影响Java不发布oracle游标,java,oracle,cursor,max,Java,Oracle,Cursor,Max,我正在编写下面的代码(为了清晰起见进行了编辑),这给了我一些关于Oracle中开放游标的问题。 基本上,我试图从数据库中选择数据,对于返回的每一行,都有0行或多行子数据要选择并附加到记录中。 目前,这是通过调用另一个函数来实现的,同时填充主数据集以读取子数据。它适用于较小的行数(小于1000行)。虽然这是用户将使用的正常操作范围,但他们可能会请求所有行,这些行的顺序可能是10行或数千行。执行大容量选择会导致ORA-01000:超出最大打开游标错误。如果我运行代码并查询v$open\u游标,则可能
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;
}
我可以增加游标的数量,但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;
}
这看起来像是1999年的PowerBuilder代码。为子级执行多个选择是一种反模式。您需要在更少的数据库调用中执行此操作。。。这是聊天的方式 由于您使用的是Oracle,因此可以尝试在检索子行和父行之前使用connect by—一次检索所有子行。这是最好的解决办法 如果您不能通过preor获得connect,那么可以将调用组合到in(id1,id2,…,idN)子句中,并分块检索它们 还可以查看resultset上的并发设置。也许你有一个可滚动的结果集
无论你如何解决它,我都会担心会炸掉虚拟机并得到OOM。您需要为搜索结果设置行限制。您可以尝试提前准备主(“主”)和子(“详细”)语句:
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();
}
}
JDBC驱动程序可能会因为在一个连接上同时运行多个结果集而阻塞。我怀疑这会导致Oracle的JDBC驱动程序出现一些错误行为(我肯定看到它在其他驱动程序中出现问题,包括刚刚关闭您的第一个结果集,Oracle显然没有这样做)。我最好是连接到标题行,读取所有对象,将它们放在一个集合中,然后遍历它们并使用单独的结果集读取细节对象
尽管JDBC规范没有说明JDBC驱动程序在这方面的任何义务,但JDBC-ODBC桥明确地只允许每个连接有一个活动语句,因此其他JDBC驱动程序当然可以自由地有类似的限制(例如每个连接只有一个打开的结果集)。您使用的是连接池吗?当您认为已关闭某些PreparedStatements时,它可能正在缓存这些语句
要检查您是否在这种情况下,请尝试(暂时)使用未准备好的语句或禁用连接池。也许您应该推荐一种方法来完成此操作。由于数据的使用和显示方式,从编码角度来看,将数据保存在两个结果集中更容易。这两个查询都只是简单的选择,所以不要给数据库带来太多麻烦,您使用的是哪个版本的数据库?哪些JDBC驱动程序(Oracle的?)和哪些版本?很抱歉,这里的信息遗漏了一点。使用ojdbc14.jar的Oracle 10.2.0.1.0选择了围绕此解决方案重新编写代码,解决了这个问题。当代码运行时,光标计数现在保持良好和稳定。谢谢大家的回答。