java.sql.SQLException:-ORA-01000:超出最大打开游标数

java.sql.SQLException:-ORA-01000:超出最大打开游标数,java,oracle,jdbc,Java,Oracle,Jdbc,我收到一个ORA-01000 SQL异常。所以我有一些相关的疑问 最大打开游标数是否与JDBC连接数完全相关,或者它们是否也与我们为单个连接创建的语句和结果集对象相关?(我们正在使用连接池) 有没有办法配置数据库中语句/结果集对象的数量(如连接) 建议在单线程环境中使用实例变量语句/resultset对象而不是方法局部语句/resultset对象吗 在循环中执行准备好的语句是否会导致此问题?(当然,我可以使用sqlBatch)注意:一旦循环结束,pStmt就会关闭 { //method try

我收到一个ORA-01000 SQL异常。所以我有一些相关的疑问

  • 最大打开游标数是否与JDBC连接数完全相关,或者它们是否也与我们为单个连接创建的语句和结果集对象相关?(我们正在使用连接池)
  • 有没有办法配置数据库中语句/结果集对象的数量(如连接)
  • 建议在单线程环境中使用实例变量语句/resultset对象而不是方法局部语句/resultset对象吗
  • 在循环中执行准备好的语句是否会导致此问题?(当然,我可以使用sqlBatch)注意:一旦循环结束,pStmt就会关闭

    { //method try starts  
      String sql = "INSERT into TblName (col1, col2) VALUES(?, ?)";
      pStmt = obj.getConnection().prepareStatement(sql);
      pStmt.setLong(1, subscriberID);
      for (String language : additionalLangs) {
        pStmt.setInt(2, Integer.parseInt(language));
        pStmt.execute();
      }
    } //method/try ends
    
    { //finally starts
       pStmt.close()
    } //finally ends 
    
  • 如果对单个连接对象多次调用conn.createStatement()和conn.prepareStatement(sql),会发生什么情况

  • 编辑1: 6.使用弱/软引用语句对象是否有助于防止泄漏

    编辑2: 1.有没有办法在我的项目中找到所有缺少的“statement.close()”语句?我知道这不是内存泄漏。但我需要找到一个符合垃圾收集条件的语句引用(其中不执行close())?有可用的工具吗?还是我必须手动分析

    请帮助我理解它

    解决方案 在Oracle DB中查找用户名为-VELU的已打开游标 转到ORACLE机器并以sysdba的身份启动sqlplus

    [oracle@db01 ~]$ sqlplus / as sysdba 
    
    然后跑

    SELECT   A.VALUE,
        S.USERNAME,
        S.SID,
        S.SERIAL#
      FROM V$SESSTAT A,
        V$STATNAME B,
        V$SESSION S
      WHERE A.STATISTIC# = B.STATISTIC#
        AND S.SID        = A.SID
        AND B.NAME       = 'opened cursors current'
        AND USERNAME     = 'VELU';
    

    如果可能,请阅读您是否将自动提交设置为true?如果没有,请尝试以下操作:

    { //method try starts  
        String sql = "INSERT into TblName (col1, col2) VALUES(?, ?)";
        Connection conn = obj.getConnection()
        pStmt = conn.prepareStatement(sql);
    
        for (String language : additionalLangs) {
            pStmt.setLong(1, subscriberID);
            pStmt.setInt(2, Integer.parseInt(language));
            pStmt.execute();
            conn.commit();
        }
    } //method/try ends { 
        //finally starts
        pStmt.close()
    } //finally ends 
    

    ORA-01000是最大开放游标错误,是Oracle数据库开发中极为常见的错误。在Java环境中,当应用程序试图打开的结果集多于数据库实例上配置的游标时,就会发生这种情况

    常见的原因有:

  • 配置错误

    • 应用程序中查询数据库的线程比数据库中的游标多。一种情况是,连接和线程池大于数据库上的游标数
    • 您有许多开发人员或应用程序连接到同一个DB实例(可能会包含许多模式),并且一起使用的连接太多
    • 解决方案:

      • 在数据库上(如果资源允许)或
      • 减少应用程序中的线程数
  • 光标泄漏

    • 应用程序没有关闭结果集(在JDBC中)或游标(在数据库的存储过程中)
    • 解决方案:光标泄漏是错误;增加DB上的游标数量只会延迟不可避免的故障。可以使用、或应用程序级日志记录和查找泄漏
  • 出身背景 本节介绍了游标背后的一些理论以及应该如何使用JDBC。如果你不需要知道背景,你可以跳过这个直接转到“消除泄漏”

    什么是光标? 游标是数据库上的一种资源,它保存查询的状态,特别是读卡器在结果集中的位置。每个SELECT语句都有一个游标,PL/SQL存储过程可以根据需要打开和使用任意多的游标。您可以在上找到有关游标的更多信息

    一个数据库实例通常服务于多个不同的模式,许多不同的用户都有多个会话。为此,它为所有模式、用户和会话提供了固定数量的游标。当所有游标都处于打开状态(正在使用中)且请求需要新游标时,请求将失败,并出现ORA-010000错误

    查找和设置游标数 该号码通常由DBA在安装时配置。可以在中的管理员功能中访问当前使用的游标数、最大数目和配置。从SQL中,可以设置为:

    ALTER SYSTEM SET OPEN_CURSORS=1337 SID='*' SCOPE=BOTH;
    
    将JVM中的JDBC与DB上的游标关联 下面的JDBC对象与以下数据库概念紧密耦合:

    • JDBC连接是数据库会话的客户端表示,并提供数据库事务。一个连接在任何时候只能打开一个事务(但事务可以嵌套)
    • 数据库上的单个游标支持JDBC结果集。在结果集上调用close()时,将释放光标
    • JDBC CallableStatement调用数据库上的存储过程,通常用PL/SQL编写。存储过程可以创建零个或多个游标,并可以将游标作为JDBC结果集返回
    JDBC是线程安全的:在线程之间传递各种JDBC对象是完全可以的

    例如,您可以在一个线程中创建连接;另一个线程可以使用此连接创建PreparedStatement,第三个线程可以处理结果集。唯一的主要限制是,在任何时候都不能在单个PreparedStatement上打开多个ResultSet。看

    请注意,数据库提交发生在连接上,因此该连接上的所有DML(插入、更新和删除)将一起提交。因此,如果希望同时支持多个事务,则必须为每个并发事务至少建立一个连接

    关闭JDBC对象 执行结果集的一个典型示例是:

    Statement stmt = conn.createStatement();
    try {
        ResultSet rs = stmt.executeQuery( "SELECT FULL_NAME FROM EMP" );
        try {
            while ( rs.next() ) {
                System.out.println( "Name: " + rs.getString("FULL_NAME") );
            }
        } finally {
            try { rs.close(); } catch (Exception ignore) { }
        }
    } finally {
        try { stmt.close(); } catch (Exception ignore) { }
    }
    
    注意finally子句如何忽略close()引发的任何异常:

    • 如果只关闭结果集而不使用try{}catch{},则可能会失败并阻止关闭语句
    • 我们希望允许在函数体中引发的任何异常尝试传播到调用方。 如果有循环,例如创建和执行语句,请记住关闭循环中的每个语句
    在Java7中,Oracle引入了,它取代了大多数Java6样板wi
    try
    { //method try starts  
      String sql = "INSERT into TblName (col1, col2) VALUES(?, ?)";
      pStmt = obj.getConnection().prepareStatement(sql);
      pStmt.setLong(1, subscriberID);
      for (String language : additionalLangs) {
        pStmt.setInt(2, Integer.parseInt(language));
        pStmt.execute();
      }
    } //method/try ends
    finally
    { //finally starts
       pStmt.close()
    } 
    
        class obj{ 
    
        public Connection getConnection(){
        return new ConnectionDelegator(...here create your connection object and put it into ...);
    
        } 
    }
    
    
    class ConnectionDelegator implements Connection{
        Connection delegates;
    
        public ConnectionDelegator(Connection con){
           this.delegates = con;
        }
    
        public Statement prepareStatement(String sql){
            return delegates.prepareStatement(sql);
        }
    
        public void close(){
            try{
               delegates.close();
            }finally{
               log.debug(delegates.toString() + " was closed");
            }
        }
    }
    
      [oracle@db01 ~]$ sqlplus / as sysdba
    
     alter session set session_cached_cursors=0
     select * from V$PARAMETER where name='session_cached_cursors'
    
     SELECT max(a.value) as highest_open_cur, p.value as max_open_cur FROM v$sesstat a, v$statname b, v$parameter p WHERE a.statistic# = b.statistic# AND b.name = 'opened cursors current' AND p.name= 'open_cursors'  GROUP BY p.value;
    
     SELECT a.value, s.username, s.sid, s.serial#
     FROM v$sesstat a, v$statname b, v$session s
     WHERE a.statistic# = b.statistic#  AND s.sid=a.sid 
     AND b.name = 'opened cursors current' AND username = 'SCHEMA_NAME_IN_CAPS'
    
     SELECT oc.sql_text, s.sid 
     FROM v$open_cursor oc, v$session s
     WHERE OC.sid = S.sid
     AND s.sid=1604
     AND OC.USER_NAME ='SCHEMA_NAME_IN_CAPS'
    
    SELECT s.machine, oc.user_name, oc.sql_text, count(1) 
    FROM v$open_cursor oc, v$session s
    WHERE oc.sid = s.sid
    and S.USERNAME='XXXX'
    GROUP BY user_name, sql_text, machine
    HAVING COUNT(1) > 2
    ORDER BY count(1) DESC
    
    java.sql.SQLException: - ORA-01000: maximum open cursors exceeded