【问题标题】:Oracle doesn't remove cursors after closing result setOracle 在关闭结果集后不删除游标
【发布时间】:2011-02-03 08:36:33
【问题描述】:

注意:我们重复使用单个连接。

************************************************
public Connection connection() {        
    try {
        if ((connection == null) || (connection.isClosed()))
        {
            if (connection!=null)
                log.severe("Connection was closed !");
            connection = DriverManager.getConnection(jdbcURL, username, password);
        }
    } catch (SQLException e) {
        log.severe("can't connect: " + e.getMessage());
    }
    return connection;        
}
**************************************************

public IngisObject[] select(String query, String idColumnName, String[] columns) {
    Connection con = connection();

    Vector<IngisObject> objects = new Vector<IngisObject>();
    try {
        Statement stmt = con.createStatement();

        String sql = query;
        ResultSet rs =stmt.executeQuery(sql);//oracle increases cursors count here
        while(rs.next()) {
            IngisObject o = new IngisObject("New Result");
            o.setIdColumnName(idColumnName);            
            o.setDatabase(this);
            for(String column: columns)
                o.attrs().put(column, rs.getObject(column));
            objects.add(o);
        }

        rs.close();// oracle don't decrease cursor count here, while it's expected
        stmt.close();
    } 
    catch (SQLException ex) {
        System.out.println(query);
        ex.printStackTrace();
    }

【问题讨论】:

  • 您指的是“ORA-01000:超出最大打开游标”吗?如果是这样,请参阅 asktom.oracle.com/pls/asktom/… 以获得解释。
  • @Oliver Michels 看起来很有帮助。您可以将其添加为答案吗?
  • 它试图写一个简短的摘要。最好的问候。
  • 数据库服务器上正在运行防病毒软件?如果您的数据库服务器上需要防病毒软件(这不是一个好习惯 - 正如许多人所指出的那样),至少在扫描数据库文件和数据库服务器二进制文件时排除。

标签: java oracle jdbc resultset database-cursor


【解决方案1】:

init.ora 参数open_cursors 定义了一个会话一次可以打开的最大游标数。它的默认值为 50。如果应用程序超过此数字,则会引发错误“ORA-01000:超出最大打开游标”。

因此,当不再需要 JDBC 资源时,必须关闭它们,尤其是 java.sql.ResultSet 和 java.sql.Statement。如果它们没有关闭,则应用程序存在资源泄漏。

在重用 Connection 对象的情况下,您必须注意打开的 oracle 游标保持打开状态并在使用中,只要连接存在 并且事务还没有结束。当应用程序提交时,打开的游标被释放。

因此,作为应用程序设计人员,您需要知道对最复杂事务所需的打开游标的粗略估计。

困难在于oracle的内部参数视图(v$open_cursor、v$sesstat等)无法显示打开的游标(可重用)和打开的游标仍被阻塞(不可重用)之间的区别!) 由未封闭的 ResulSet 或语句。如果您关闭 finally 块中的所有 Statement 和 ResultSet 对象,您的应用程序就完美了。

调整init.ora参数是这样的(我们的应用最多需要800个游标)

ALTER SYSTEM SET open_cursors = 800 SCOPE=BOTH;

【讨论】:

  • 我认为应该强调一个必须关闭 ResultSet AND 语句。
  • 嗨,我有一个 DBconnection,open_cursor=150,autocommit=false。我关闭了所有 PreparedStatement 和 ResultSet 但我遇到了这个问题。这可能是因为触发器在一些删除和插入之后开始,但是在提交之后插入可能是危险的,因为在那个时候提交了所有事务,如果然后在程序的其他部分失败,我不能整个事务的真正回滚。有什么建议吗?
  • @Andrea_86:在触发器中提交是您通往地狱的直接途径;-) 尝试增加您的 open_cursors 设置。
  • 非常有帮助。但是对于任何类型的企业应用程序来说,8000 的大小都比 800 更好
【解决方案2】:

正确的做法是关闭 finally 块中的每个资源在它自己的 try/catch 块中。我通常使用这样的静态实用程序类:

public class DatabaseUtils
{
    public static void close(Connection connection)
    {
        try
        {
            if (connection != null)
            {
                connection.close();
            }
        }
        catch (SQLException e)
        {
            // log exception here.
        }
    }

    // similar methods for ResultSet and Statement
}

所以我会这样写你的代码:

public IngisObject[] select(String query, String idColumnName, String[] columns) {

Vector<IngisObject> objects = new Vector<IngisObject>();

Connection con = null;
Statement stmt = null;
ResultSet rs = null;

try 
{
    connection = connection();
    stmt = con.createStatement();

    // This is a SQL injection attack waiting to happen; I'd recommend PreparedStatemen
    String sql = query;
    rs =stmt.executeQuery(sql);//oracle increases cursors count here
    while(rs.next()) 
    {
       IngisObject o = new IngisObject("New Result");
       o.setIdColumnName(idColumnName);            
       o.setDatabase(this);
       for(String column: columns) o.attrs().put(column, rs.getObject(column));
       objects.add(o);
    }

} 
catch (SQLException ex) 
{
    System.out.println(query);
    ex.printStackTrace();
}
finally
{
    DatabaseUtils.close(rs);
    DatabaseUtils.close(stmt);
    DatabaseUtils.close(con);
}

【讨论】:

  • 如何从 Java 代码的 psvm 主方法执行 public IngisObject[] select(String query, String idColumnName, String[] columns)。你能把它也包括在你的代码中吗?sn-p
  • 简单 - 我将把它作为练习留给你。您可以将 List 转换为数组 - 调用我编写的方法并返回一个数组。
【解决方案3】:

通常,您会将 ResultSet 和 Statement 的 close 语句放入 finally 块中,以确保即使发生异常也能调用它们(可能是您在此处遇到的问题)。在您当前的代码中,如果发生 SQLException,则两个 close( ) 方法调用将永远不会发生,并且游标将保持打开状态。

另外,您在 Oracle 中使用什么查询来查看打开游标的计数?

编辑:
该代码应该关闭游标。如果不是,那么您应该能够看到调用您的方法和游标计数增加 1 的 1 对 1 相关性。确保没有导致游标计数增加的意外过程。

如果您有权限,您可以对数据库运行此查询,以通过 sid 查看打开的游标计数,以查看是否是其他进程增加了游标,而不是您的特定进程。它会拉回任何打开超过 10 个光标的内容,您可以提高它以过滤掉噪音或通过用户名或 osuser 专门缩小它:

select oc.sid,
       count(*) numCur,
       s.username username,
       s.osuser osuser,
       oc.sql_text,
       s.program
  from v$open_cursor oc,
       v$session s
 where s.sid = oc.sid
group by oc.sid, 
         oc.sql_text, 
         s.username, 
         s.osuser, 
         s.program
having count(*) > 10
order by oc.sid;

另一个可能有用的查询,以防多个 sid 使用相同的查询字符串,因此上述内容不能很好地揭示违规者:

 select oc.sql_text, count(*) 
   from v$open_cursor oc 
   group by oc.sql_text 
   having count(*) > 10 
   order by count(*) desc;

【讨论】:

  • 我知道最好最后关闭它们。但 excpetion 不是问题。在我的测试中没有发生。
  • @Vladimir:我在答案中添加了其他信息。如果没有抛出异常,您的代码应该可以工作。查看 sid 级别并确保只有您的进程会影响游标计数。
  • 这个查询帮我找出了问题(最后,我不小心“泄漏”了java中的preparedstatements,哎呀)
【解决方案4】:

我刚遇到同样的问题,发现 - 如果你不关闭连接(因为你以后可能会重复使用它) - 你至少必须做一个connection.rollback() 或 @ 987654322@ 在关闭 ResultSet 和 Statements 的同时释放打开的游标。

【讨论】:

  • 我与您的观点相矛盾,最好关闭您的 ResultSet 而不是依靠垃圾收集器为您完成。
  • @Amandeep Jiddewar 当然,您应该关闭 ResultSet。但是由于问题是关于关闭 ResultSet 后打开的游标,我刚刚指出您还必须执行连接回滚/提交以释放游标。所以没什么可矛盾的:-)
  • 就我而言,我不得不立即关闭连接。提交/回滚并没有太大帮助。
猜你喜欢
  • 2019-02-19
  • 1970-01-01
  • 1970-01-01
  • 2019-09-11
  • 2019-09-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多