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();
}
Note: we reuse single connection.
************************************************
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();
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
init.ora 参数
open_cursors
定义会话一次可以打开的游标的最大数量。它的默认值为 50。如果应用程序超过此数字,则会出现错误“ORA-01000:超出最大打开游标数”。因此,当不再需要 JDBC 资源时,必须关闭它们,特别是 java.sql.ResultSet 和 java.sql.Statement。如果它们没有关闭,应用程序就会发生资源泄漏。
在重用 Connection 对象的情况下,您必须注意这样一个事实:只要连接存在且事务尚未结束,打开的 Oracle 游标就会保持打开状态并处于使用状态。当应用程序提交时,打开的游标将被释放。
因此,作为应用程序设计人员,您需要了解最复杂的事务所需的打开游标的粗略估计。
困难在于oracle的内部参数视图(v$open_cursor、v$sesstat等)无法显示打开的游标(可重用)和打开的游标(仍然被阻止(不可重用!))之间的区别。未封闭的结果集或语句。如果您关闭finally 块中的所有Statement 和ResultSet 对象,您的应用程序就完全没问题。
调整init.ora参数的工作方式如下(我们的应用程序最多需要800个游标)
The init.ora parameter
open_cursors
defines the maximum of opened cursors a session can have at once. It has a default value of 50. If the application exceeds this number the error "ORA-01000: maximum open cursors exceeded" is raised.Therefore it's mandatory to close the JDBC resources when they are not needed any longer, in particular java.sql.ResultSet and java.sql.Statement. If they are not closed, the application has a resource leak.
In case of reusing the Connection object, you must be aware of the fact that the opened oracle cursors are kept open and in use as long the connection exists and the transaction has not ended. When the application commits, the opened cursors are released.
Therefore as an application designer you need to know a rough estimation of the needed open cursors for your most complex transaction.
The difficulty lies in the inability of oracle's internal parameter views (v$open_cursor, v$sesstat, et. al.) to show the difference between opened cursors, which are reusable and opened cursors, which are still blocked (not reusable!) by an unclosed ResulSet or Statement. If you close all Statement and ResultSet objects in your finally block, your application is perfectly fine.
Adjusting the init.ora parameter works like this (our application needs 800 cursors at a maximum)
正确的方法是在自己的 try/catch 块中关闭 finally 块中的每个资源。我通常使用这样的静态实用程序类:
所以我会这样编写代码:
The correct way to do it is to close every resource in a finally block in its own try/catch block. I usually use a static utility class like this:
So I'd write your code like this:
通常,您会将 ResultSet 和 Statement 的关闭语句放入
finally
块中,以确保即使发生异常(可能是您在这里遇到的问题),它们也会被调用。在当前代码中,如果发生 SQLException,则两个 close() 方法调用将永远不会发生,并且游标将保持打开状态。另外,您在 Oracle 中使用什么查询来查看打开游标的计数?
编辑:
该代码应该关闭光标。如果不是,那么您应该能够看到调用您的方法和游标计数增加 1 的 1 比 1 相关性。确保没有某些意外进程导致游标计数增加。
如果您有权限,您可以对数据库运行此查询,以查看 sid 的打开游标计数,看看是否是其他进程在增加游标,而不是您的进程。它将拉回打开超过 10 个游标的任何内容,您可以提高它来过滤噪音或通过用户名或 osuser 专门缩小范围:
另一个可能有用的查询,以防多个 sid 使用相同的查询字符串,因此上面的没有很好地揭露罪犯:
Normally you would put the close statements for your ResultSet and Statement into a
finally
block to ensure that they are called even if an exception occurs (could be the issue you are having here). In your current code, if a SQLException occurs then the two close( ) method calls will never occur and cursors would be left open.Also what query are you using in Oracle to see the count of open cursors?
Edit:
That code should be closing the cursor. If it isn't then you should be able to see a 1 to 1 correlation of calling your method and the cursor count going up by 1. Be sure there isn't some unexpected process that is causing the cursor count to go up.
If you have the privileges, you can run this query against the database to see the open cursor count by sid to see if maybe it is some other process that is increasing the cursors and not yours specifically. It will pull back any with more than 10 cursors open, you can raise this to filter out the noise or narrow it specifically by username or osuser:
Another query that may be helpful, in case multiple sid's are using the same query string so the above does not reveal the offender well:
我刚刚遇到了同样的问题,并发现 - 如果您不关闭连接(因为您稍后可能会重用它)-您至少必须执行 connection.rollback() 或 connection.commit() 释放打开的游标并关闭 ResultSet 和声明。
I just had the same problem and found that - if you do not close the connection (because you will maybe reuse it later on) - you at least have to do a connection.rollback() or connection.commit() to free the open cursors togehther with closing the ResultSet and Statements.