我们使用Spring SimpleJdbcCall来调用返回游标的Oracle中的存储过程。看起来SimpleJdbcCall没有关闭游标,过了一段时间就超出了最大打开游标。
ORA-01000: maximum open cursors exceeded ; nested exception is java.sql.SQLException: ORA-01000: maximum open cursors exceeded spring
在论坛上还有一些人经历过这个但似乎没有答案。看起来我是spring / oracle支持中的一个bug。
此错误至关重要,可能会影响我们将来使用Spring JDBC。
是否有人遇到过修复 - 要么将问题跟踪到Spring代码,要么找到避免此问题的解决方法?
我们正在使用Spring 2.5.6。
以下是使用SimpleJdbcCall的代码的新版本,它似乎无法正确关闭proc通过游标返回的结果集:
...
SimpleJdbcCall call = new SimpleJdbcCall(dataSource);
Map params = new HashMap();
params.put("remote_user", session.getAttribute("cas_username") );
Map result = call
.withSchemaName("urs")
.withCatalogName("ursWeb")
.withProcedureName("get_roles")
.returningResultSet("rolesCur", new au.edu.une.common.util.ParameterizedMapRowMapper() )
.execute(params);
List roles = (List)result.get("rolesCur")
不使用Spring JDBC的旧版本代码没有这个问题:
oracleConnection = dataSource.getConnection();
callable = oracleConnection.prepareCall(
"{ call urs.ursweb.get_roles(?, ?) }" );
callable.setString(1, (String)session.getAttribute("cas_username"));
callable.registerOutParameter (2, oracle.jdbc.OracleTypes.CURSOR);
callable.execute();
ResultSet rset = (ResultSet)callable.getObject(2);
... do stuff with the result set
if (rset != null) rset.close(); // Explicitly close the resultset
if (callable != null) callable.close(); //Close the callable
if (oracleConnection != null) oracleConnection.close(); //Close the connection
看起来Spring JDBC不会调用rset.close()。如果我在旧代码中注释掉该行,那么在负载测试之后我们会得到相同的数据库异常。
答案 0 :(得分:8)
经过多次测试后我们解决了这个问题。它是我们如何使用spring框架和oracle客户端以及oracle DB的组合。我们正在创建新的SimpleJDBCCalls,它们使用oracle JDBC客户端的元数据调用,这些调用作为游标返回,而这些游标未被关闭和清理。我认为这是Spring JDBC框架中一个如何调用元数据然后不关闭游标的错误。 Spring应该从光标中复制元数据并正确关闭它。我没有打算用spring打开jira问题,因为如果你使用最佳实践,那么bug就不会出现。
调整OPEN_CURSORS或任何其他参数是解决此问题的错误方法,只是延迟它出现。
我们通过将SimpleJDBCCall移动到单个DAO来解决它/修复它,因此我们调用的每个oracle proc只打开一个游标。这些游标在应用程序的生命周期内是开放的 - 我认为这是一个错误。只要OPEN_CURSORS大于SimpleJDBCCall对象的数量,就不会有麻烦。
答案 1 :(得分:3)
好吧,当我读BLOB时,我遇到了这个问题。主要原因是我也在更新表,并且包含update子句的Statement没有自动关闭。令人讨厌的光标漏掉了所有免费游标。在明确调用statement.close()之后,错误消失了。
道德 - 总是关闭所有内容,处理Statement后不依赖自动关闭。
答案 2 :(得分:2)
请注意将OPEN_CURSORS设置为越来越高的值,因为存在开销,它可能只是对代码中的实际问题/错误进行了频带辅助。
我没有使用Spring方面的经验,但在一个应用程序上工作,我们遇到ORA-01000错误的许多问题,并且不断调整OPEN_CURSORS只是让问题消失了一段时间......
答案 3 :(得分:1)
我可以向你保证,这不是春天。我参与了一个Spring 1.x应用程序,该应用程序于2005年上线,并且从那时起就没有泄露连接。 (WebLogic 9.,JDK 5)。您没有正确关闭资源。
您使用的是连接池吗?您正在部署哪个应用服务器?哪个版本的Spring?甲骨文? Java的?细节,请。
答案 4 :(得分:-2)
Oracle OPEN_CURSORS是关键。我们有一个针对Oracle XE运行的小型24x7应用程序,只有几个明显开放的游标。在我们将OPEN_CURSORS初始化值设置为>之前,我们有间歇性的最大打开游标错误。 300
答案 5 :(得分:-4)
解决方案不在Spring中,而是在Oracle中:您需要将OPEN_CURSORS
初始化参数设置为高于默认值50的某个值。
Oracle - 至少从8i开始,或许它已经改变了 - 会重新解析JDBC PreparedStatement对象,除非你把它们打开。这很昂贵,大多数人最终会维护一个重新提交的固定语句池。
(快速查看10i文档,他们明确指出OCI驱动程序将缓存PreparedStatements,因此我假设本机驱动程序仍然每次都重新创建它们)