Oracle 在关闭结果集后不删除游标

新手上路,请多包涵

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

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

原文由 Vladimir 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 881
2 个回答

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

因此,当不再需要 JDBC 资源时必须关闭它们,特别是 java.sql.ResultSet 和 java.sql.Statement。如果它们没有关闭,应用程序就会发生资源泄漏。

在重用 Connection 对象的情况下,您必须意识到,只要连接存在 事务尚未结束,打开的 oracle 游标就会保持打开状态并处于使用状态。当应用程序提交时,打开的游标被释放。

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

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

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

 ALTER SYSTEM SET open_cursors = 800 SCOPE=BOTH;

原文由 Oliver Michels 发布,翻译遵循 CC BY-SA 2.5 许可协议

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

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

原文由 duffymo 发布,翻译遵循 CC BY-SA 2.5 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题