Java 甲骨文没有';t关闭结果集后删除游标

Java 甲骨文没有';t关闭结果集后删除游标,java,oracle,jdbc,cursor,resultset,Java,Oracle,Jdbc,Cursor,Resultset,注意:我们重用单个连接 ************************************************ public Connection connection() { try { if ((connection == null) || (connection.isClosed())) { if (connection!=null) log.severe("Conne

注意:我们重用单个连接

************************************************
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();
    }
************************************************
公共连接(){
试一试{
if((connection==null)| |(connection.isClosed()))
{
if(连接!=null)
严重(“连接已关闭!”);
connection=DriverManager.getConnection(jdbcURL、用户名、密码);
}
}捕获(SQLE异常){
log.severy(“无法连接:+e.getMessage());
}
回路连接;
}
**************************************************
public IngisObject[]选择(字符串查询、字符串idColumnName、字符串[]列){
连接con=连接();
向量对象=新向量();
试一试{
语句stmt=con.createStatement();
字符串sql=query;
ResultSet rs=stmt.executeQuery(sql);//oracle在此处增加游标计数
while(rs.next()){
IngisObject o=新的IngisObject(“新结果”);
o、 setIdColumnName(idColumnName);
o、 setDatabase(this);
for(字符串列:列)
o、 attrs().put(列,rs.getObject(列));
添加(o);
}
rs.close();//oracle不会在此处减少光标计数,这是预期的
stmt.close();
} 
catch(SQLException-ex){
System.out.println(查询);
例如printStackTrace();
}

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

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

编辑:
该代码应该正在关闭光标。如果没有关闭,则您应该能够看到调用方法与光标计数之间的1:1相关性上升1。确保没有任何意外的过程导致光标计数上升

如果您有权限,您可以对数据库运行此查询,以查看按sid打开的游标计数,以查看是否是其他进程增加了游标,而不是您的进程。如果打开的游标超过10个,它将回调任何游标,您可以通过使用用户名或奥瑟尔:

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;

正确的方法是在自己的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);
}
所以我会这样写你的代码:

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);
}
public IngisObject[]选择(字符串查询、字符串idColumnName、字符串[]列){
向量对象=新向量();
连接con=null;
语句stmt=null;
结果集rs=null;
尝试
{
连接=连接();
stmt=con.createStatement();
//这是一个即将发生的SQL注入攻击;我推荐PreparedStatemen
字符串sql=query;
rs=stmt.executeQuery(sql);//oracle在此处增加游标数
while(rs.next())
{
IngisObject o=新的IngisObject(“新结果”);
o、 setIdColumnName(idColumnName);
o、 setDatabase(this);
for(String column:columns)o.attrs().put(column,rs.getObject(column));
添加(o);
}
} 
catch(SQLException-ex)
{
System.out.println(查询);
例如printStackTrace();
}
最后
{
数据库关闭(rs);
DatabaseUtils.close(stmt);
数据库utils.close(con);
}

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

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

在重用Connection对象的情况下,您必须了解这样一个事实:只要连接存在且事务尚未结束,打开的oracle游标将保持打开并在使用中。当应用程序提交时,打开的游标将被释放

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

困难在于oracle的内部参数视图(v$open_cursor,v$sesstat,et.al.)无法显示可重用的已打开游标和仍被阻止(不可重用!)的已打开游标之间的差异通过未关闭的ResultSet或语句。如果关闭finally块中的所有语句和ResultSet对象,则应用程序完全正常

调整init.ora参数的工作方式如下(我们的应用程序最多需要800个游标)


我只是遇到了同样的问题,并发现-如果你不关闭连接(因为你以后可能会重新使用它)-您至少需要执行一个或来释放打开的游标,同时关闭结果集和语句。

我知道最好最后关闭它们。但是,执行不是问题。在我的测试中不会发生这种情况。@Vladimir:我在回答中添加了其他信息。如果没有引发异常,您的代码应该可以工作。请看t检查sid级别,并确保影响游标计数的是您自己的进程。此查询帮助我解决了问题(最后,我意外地在java中“泄漏”了preparedstatements,oops)。您指的是“ORA-01000:超过最大打开游标数”吗?如果是,请参阅以获取解释。@Oliver Mich