“java.sql.SQLException: ORA-01000: 超出打开游标的最大数”的解决方法

680 阅读2分钟

前言

今天在开发一个自动根据数据库表来生成对应实体类java文件的模块时发现一个问题,如果一个数据库中的表不是太多使用就不会有问题,一但表数量太多就会出现问题,会报一个“java.sql.SQLException: ORA-01000: 超出打开游标的最大数”异常。

问题描述

现象:

image.png

根据堆栈信息看源码:

image.png

报错原因

报错的原因就是在循环中使用了DatabaseMetaData.getColumns()方法,那么为什么在循环中使用这个会报错呢,这个DatabaseMetaData是不是也想satement对象一样可以关闭呢?

查看了一下ojdbc的源码,发现oracle在实现的时候并没有去关闭statement的操作,

CallableStatement var11 = this.connection.prepareCall(var6, var10);  // 从connection中获取到了一个statement.
var11.setString(1, var7);
var11.setString(2, var8);
var11.setString(3, var9);
var11.registerOutParameter(4, -10);
var11.closeOnCompletion();
var11.execute(); // 执行

如图ojdbc中并没有关闭的动作。

image.png 同样的看了下mysql中jdbc源码,mysql的是实现了关闭statement的。

image.png

所以同样的表数量使用mysql是没有任何问题的。突然发现我发现的这个问题可能是一个ojdbc的bug.但是看网上还没有人提出来。可能也是我没有百度到。

问题解决

查询了相关资料,这种问题一般有如下几种解决方式:
1、需要使用完connect之后立即关闭connect,但是这样使用会带来很大的开销,原因是频繁的创建连接对数据库本身和应用都是一个耗时的操作。
2、另一种方式就是使用alter system set open_cursors=2000 scope=both;来修改数据库的游标数,但是这样会涉及到数据库的操作,需要专业的人员去评估可行性。
3、当然我还想到一个方式,就是通过ASM或者javaissit等第三方库去为oracle.jdbc.driver.OracleDatabaseMetaData#getColumns类做一个动态代理,去动态的做增强在方法的结尾添加var11.close()操作。

总结

综上,如果在循环中使用DatabaseMetaData.getColumns()方法且连接的数据库是mysql则不会有问题,如果使用的是oracel就会报错。