我们也遇到过这个问题,有两个办法:
1是把所有或嫌疑比较大的preparestatement改成statement
2是按照下面的办法查找代码中游标泄漏的部分
大多数使用Oracle的程序员都会遇到另一个不充足的数据库资源:游标。Oracle为每一个SQL SELECT语句都创建一个数据库游标,并且维护那个光标直到应用程序关闭该语句。在JDBC应用程序中,关闭该语句就意味着调用用来运行该SQL语句的Statement对象 或 PreparedStatement对象的close()方法。从一个现存的Statement对象或PreparedStatement对象执行一个新的查询也会关闭与前一个查询相关的游标。
考虑下面的代码:
PreparedStatement pstmt = dbCon.prepareStatement("select * from emp where emp_id=?");
pstmt.setInt(1, 422);
ResultSet rs = pstmt.executeQuery();
// processing of result set
pstmt = dbCon.prepareStatement("select * from customers where cust_id = ?");
// etc
上面的代码段有一个主要的问题。如果该代码是用普通的Statement对象写的,那么它很可能已经通过多次调用executeQuery(String)方法创建了ResultSet对象。但是,因为上面的代码使用的是PreparedStatement对象而不是普通的Statement对象,它需要显式地关闭第一个对象而不是简单地在同一个地方创建一个新的对象。上面的代码,在连接自身被关闭之前将会用"select * from emp where emp_id=?"游标扰乱数据库。
尽管这个bug似乎相当明显,Java和Oracle的程序员往往都假设这些资源会被自动释放。由于Oracle JDBC驱动器的体系结构,Java垃圾收集器(garbage collector)不会清理那些孤立的PreparedStatement对象,并且,在大容量的应用程序中游标溢出(cursor leak)会很快累积。因此,假设PreparedStatement对象会自动释放是程序员最常犯的错误之一。
如果你已经访问了SYS.V_$OPEN_CURSOR视图,下面的SQL语句能够容易地发现游标溢出:
select user_name, sql_text, count(*) from sys.v_$open_cursor
group by user_name, sql_text
如果你在一个常规的查询或一个触发器中单独使用该语句执行PL/SQL过程,那么关闭Statement对象尤为重要。如果PL/SQL过程创建了多个隐式游标,在父语句被关闭之前,即使它们的工作完成了,它们有时也不能被恰当地关闭。