ORA-01000: 超出打开游标的最大数(解决及原因)
还是 chongqingdaxue 缴费平台, 税票打印完毕, 上传到财务处时, 当税票数目较少时(10几张), 能正常上传; 当数目较大时(共408张), 就上传失败. 查看 tomcat 日志, 如下:
既然超出最大游标数, 为了第一间让程序恢复正常运行, 当然"脚疼治脚", 先把最大游标数调大:
(1). 以 DBA 登录 PL/SQL
(2). 打开 Command Window(注意不是 SQL Window)
(3). 输入以下命令, 修改 oracle 最大游标数为 1000
1
SQL>
alter
system
set
open_cursors=1000 scope=both;
(4). 查看最大游标数是否已修改成功
1
SQL> show parameter open_cursors;
(5). 重启 tomcat(一定要有这一步, 否则修改不生效)
经过以上操作, 票据顺利上传上去了, 可是这样治标不治本, 万一以后来个1000张以上的票据要上传, 那不就又得改游标数了; 在代码里肯定是每处理一张票就打开一个游标的, 这样是错误的. 网上找了这个错误代码的相关说明, 在这里发现以下这段话
这样的错误很容易出现在Java代码中的主要原因是:Java代码在执行conn.createStatement()和 conn.prepareStatement()的时候,实际上都是相当与在数据库中打开了一个cursor。尤其是,如果你的 createStatement和prepareStatement是在一个循环里面的话,就会非常容易出现这个问题。因为游标一直在不停的打开,而且没有关闭。
在代码里看了一下, 果然是这样, 程序通过循环调用封装好的 executeSql 函数, 来对每张票进行 update, 而 executeSql 里每次都调用了 conn.prepareStatement(); 将其移出循环, 再改回原游标数, 再上传票据, 就不再报错了.
01
public
static
void
executeSql(Connection conn, String sql, Object[] parValue)
throws
Exception{
02
PreparedStatement ps =
null
;
03
try
{
04
LOGGER.info(sql);
05
ps = conn.prepareStatement(sql);
06
if
(parValue !=
null
){
07
String params =
""
;
08
for
(
int
i =
0
; i < parValue.length; i++){
09
if
(i !=
0
){
10
params +=
","
;
11
}
12
ps.setObject(i+
1
, parValue[i]);
13
params += parValue[i];
14
}
15
LOGGER.info(params);
16
}
17
ps.execute();
18
}
catch
(SQLException e) {
19
e.printStackTrace();
20
throw
new
Exception();
21
}
finally
{
22
if
(ps !=
null
){
23
ps.close();
24
}
25
}
26
}
注: 原代码有进行 ps.close(); 的, 但是仍然无效, 是因为 connection 的 autocommit 设为 false 的原因? 还是因为 connection 是从连接池取的原因呢?
这里有一个问题说明 connection 与 prepareStatement 进行 close 的关系, 但这里只是说明了直接 close connection, 而没有说明直接 close prepareStatement.
问:
用CONNECTION 创建了 PREPAREDSTATEMENT ,用完了 PREPAREDSTATEMENT 后,没有关闭PREPAREDSTATEMENT ,而是直接 CLOSE CONNECTION ,这么做有什么隐患吗?
答:
如果没有使用数据库连接池,而是每次创建物理连接,然后释放的话,没有什么问题。close Connection的时候已经把数据库资源完全释放掉了,PreparedStatement占用的数据库游标也会随即释放。 但是大部分情况读写数据库都会采用数据库连接池来提高连接效率,在这种情况下有潜在的隐患。 因为数据库连接池中拿到一个Connection,close的时候不是真正关闭连接,释放数据库资源,而是把连接归还给连接池。因此在这种情况下,close了Connection,但是PreparedStatement并没有被释放掉,占用的数据库游标仍然处于打开状态。因此在大数据访问量的情况下很容易出现数据库游标使用到最大,无法分配游标错误。
ORA-01000: 超出打开游标的最大数(解决及原因)
还是 chongqingdaxue 缴费平台, 税票打印完毕, 上传到财务处时, 当税票数目较少时(10几张), 能正常上传; 当数目较大时(共408张), 就上传失败. 查看 tomcat 日志, 如下:
既然超出最大游标数, 为了第一间让程序恢复正常运行, 当然"脚疼治脚", 先把最大游标数调大:
(1). 以 DBA 登录 PL/SQL
(2). 打开 Command Window(注意不是 SQL Window)
(3). 输入以下命令, 修改 oracle 最大游标数为 1000
1 | SQL> alter system set open_cursors=1000 scope=both; |
(4). 查看最大游标数是否已修改成功
1 | SQL> show parameter open_cursors; |
(5). 重启 tomcat(一定要有这一步, 否则修改不生效)
经过以上操作, 票据顺利上传上去了, 可是这样治标不治本, 万一以后来个1000张以上的票据要上传, 那不就又得改游标数了; 在代码里肯定是每处理一张票就打开一个游标的, 这样是错误的. 网上找了这个错误代码的相关说明, 在这里发现以下这段话
这样的错误很容易出现在Java代码中的主要原因是:Java代码在执行conn.createStatement()和 conn.prepareStatement()的时候,实际上都是相当与在数据库中打开了一个cursor。尤其是,如果你的 createStatement和prepareStatement是在一个循环里面的话,就会非常容易出现这个问题。因为游标一直在不停的打开,而且没有关闭。
在代码里看了一下, 果然是这样, 程序通过循环调用封装好的 executeSql 函数, 来对每张票进行 update, 而 executeSql 里每次都调用了 conn.prepareStatement(); 将其移出循环, 再改回原游标数, 再上传票据, 就不再报错了.
01 | public static void executeSql(Connection conn, String sql, Object[] parValue) throws Exception{ |
02 | PreparedStatement ps = null ; |
03 | try { |
04 | LOGGER.info(sql); |
05 | ps = conn.prepareStatement(sql); |
06 | if (parValue != null ){ |
07 | String params = "" ; |
08 | for ( int i = 0 ; i < parValue.length; i++){ |
09 | if (i != 0 ){ |
10 | params += "," ; |
11 | } |
12 | ps.setObject(i+ 1 , parValue[i]); |
13 | params += parValue[i]; |
14 | } |
15 | LOGGER.info(params); |
16 | } |
17 | ps.execute(); |
18 | } catch (SQLException e) { |
19 | e.printStackTrace(); |
20 | throw new Exception(); |
21 | } finally { |
22 | if (ps != null ){ |
23 | ps.close(); |
24 | } |
25 | } |
26 | } |
注: 原代码有进行 ps.close(); 的, 但是仍然无效, 是因为 connection 的 autocommit 设为 false 的原因? 还是因为 connection 是从连接池取的原因呢?
这里有一个问题说明 connection 与 prepareStatement 进行 close 的关系, 但这里只是说明了直接 close connection, 而没有说明直接 close prepareStatement.
问:
用CONNECTION 创建了 PREPAREDSTATEMENT ,用完了 PREPAREDSTATEMENT 后,没有关闭PREPAREDSTATEMENT ,而是直接 CLOSE CONNECTION ,这么做有什么隐患吗?
答:
如果没有使用数据库连接池,而是每次创建物理连接,然后释放的话,没有什么问题。close Connection的时候已经把数据库资源完全释放掉了,PreparedStatement占用的数据库游标也会随即释放。 但是大部分情况读写数据库都会采用数据库连接池来提高连接效率,在这种情况下有潜在的隐患。 因为数据库连接池中拿到一个Connection,close的时候不是真正关闭连接,释放数据库资源,而是把连接归还给连接池。因此在这种情况下,close了Connection,但是PreparedStatement并没有被释放掉,占用的数据库游标仍然处于打开状态。因此在大数据访问量的情况下很容易出现数据库游标使用到最大,无法分配游标错误。