PL / SQLcall中的游标泄漏SpringMVC jdbcTemplate

时间:2018-04-10 11:32:17

标签: java oracle spring-mvc jdbc plsql

我创建了一个函数来调用for内的PL / SQL并且它正在创建游标泄漏.PL / SQL工作正常,它返回所需的数据,但我注意到游标的数量增加,直到ora超过-1000个最大打开游标和ORA-00604:在递归SQL级别1错误时发生错误。为了检查使用过的游标数,我使用了以下SQL语句:

select * 
from
 (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')
where sid = 'mySID';

我调试了我的代码,我发现游标是在这里创建的: objectXStructResult.getAttributes()[X] 。我试图关闭结果集和callableStatement但它不起作用。任何人都可以帮助我吗?

    function callToDao(){
      for(Object X:LstObject){
        plsqlCall(X);
      }
    }

    funcion ObjectX plsqlCall(Object X){
        Object salidaX = null;
        //Obtención de parámetros de consulta
        final String p_x1 = X.getX();
        final String p_x2 = X.getX();
        final String p_x3 = X.getX();
        final String p_x4 = X.getX();
        final String p_x5 = X.getX();
        final String p_x6 = X.getX();
        final String p_x7 = X.getX();

        try{
            CallableStatementCreator csCreator = new CallableStatementCreator() {

                @Override
                public CallableStatement createCallableStatement(Connection con) throws SQLException {
                    CallableStatement cs = null;
                    try {

                        cs = con.prepareCall("{call PK_XXXXX.XXXXXX(?, ?, ?, ?, ?, ?, ?, ?) }");

                        cs.setInt(1, Integer.parseInt(p_x1));                        
                        cs.setString(2, p_x2);                          
                        cs.setString(3, p_x3);                          
                        cs.setLong(4, Long.parseLong(p_x4));                           
                        cs.setDouble(5,Double.parseDouble(p_x5));                           
                        cs.setDouble(6, Double.parseDouble(p_x6));                          
                        cs.setDouble(7, Double.parseDouble(p_x7));

                        cs.registerOutParameter(8, OracleTypes.STRUCT,"OBJECT_PLSQL");

                    } catch (SQLException e) {
                        e.printStackTrace();                
                    }
                    return cs;
                }
            };
            CallableStatementCallback csCallback = new CallableStatementCallback() {

                public Object doInCallableStatement(CallableStatement cs) throws SQLException {
                    ObjectX ret = null;
                    ResultSet rs = null;
                    try {
                        cs.execute();
                        ret = obtainObjectX(cs, rs, objectPos, type);
                    } catch (SQLException e) {
                        e.printStackTrace();                
                    }
                    return ret;
                }
            };

            salida = (ObjectX) this.jdbcTemplate.execute(csCreator,csCallback);
        }catch (Exception e) {
            e.printStackTrace();
        }

        return salida;
    }

       private ObjectX obtainObjectX(CallableStatement cs, ResultSet rs, int objectPos, String p_tipo) throws SQLException{

        ObjectX objectX= new ObjectX();
        List<ObjectX> objectXLst= new ArrayList<ObjectX>();

        try{
            //Obtain exit parameter
            STRUCT objectXStructResult = (STRUCT)cs.getObject(objectPos);   

            //Obtain Struct data
            String att1 = (String)objectXStructResult.getAttributes()[1];
            String att2 = (String)objectXStructResult.getAttributes()[5];

            BigDecimal att3 = (BigDecimal)objectXStructResult.getAttributes()[6];
            String att4 = (String)objectXStructResult.getAttributes()[7];

            //Control de error en la obtención de datos
            if (new BigDecimal("0").equals(att3)){ 
                //Obtención de los datos de salida
                ARRAY listaDatos = (ARRAY)objectXStructResult.getAttributes()[0];
                if (listaDatos!=null){
                    rs = listaDatos.getResultSet();

                    int rowNum = 0;
                    //Recorrido del listado de datos
                    while (rs.next()) {
                        STRUCT dataStruct= (STRUCT) listaDatos.getOracleArray()[rowNum];
                        ObjectX objXFor= this.mapRow(dataStruct, rowNum);
                        objectXLst.add(objXFor);
                        rowNum++;
                    }
                }
                objectX.setAtt1(att1);
                objectX.setAtt2(att2);

                objectX.setAtt3(new Long(att3.toString()));
                objectX.setAtt4(att4);
                objectX.setData(objectXLst); 

            }
        } catch (Exception e) {
            e.printStackTrace();            
        }

        return objectX;
    }


      private ObjectX mapRow(STRUCT dataStruct, int rowNum) throws SQLException {
        Object[] objectInfo = dataStruct.getAttributes();

        //Obtención de datos de la estructura de base de datos
        BigDecimal att1= ((BigDecimal)objectInfo[0]);
        BigDecimal att2= (BigDecimal)objectInfo[1];
        String att3= (String)objectInfo[2];
        String att4= (String)objectInfo[3];

        return new ObjectX(att1, att2, null, att3, null, null, att4, null);
    }

更新1:

我已经设法将游标的创建从5改为1改变代码,但我仍然无法找到关闭游标的方法。

自:

        String att1 = (String)objectXStructResult.getAttributes()[1];
        String att2 = (String)objectXStructResult.getAttributes()[5];

        BigDecimal att3 = (BigDecimal)objectXStructResult.getAttributes()[6];
        String att4 = (String)objectXStructResult.getAttributes()[7];

        //Control de error en la obtención de datos
        if (new BigDecimal("0").equals(att3)){ 
            //Obtención de los datos de salida
            ARRAY listaDatos = (ARRAY)objectXStructResult.getAttributes()[0];
        }

要:

        Object[] atributos = objectXStructResult.getAttributes();
        String att1 = (String)atributos[1];
        String att2 = (String)atributos[5];

        BigDecimal att3 = (BigDecimal)atributos[6];
        String att4 = (String)atributos[7];

        //Control de error en la obtención de datos
        if (new BigDecimal("0").equals(att3)){ 
            //Obtención de los datos de salida
            ARRAY listaDatos = atributos[0];
        }

1 个答案:

答案 0 :(得分:0)

  

注意:应该在回调实现中的finally块中关闭打开的任何ResultSet。 Spring将在返回回调后关闭Statement对象,但这并不一定意味着ResultSet资源将被关闭:Statement对象可能被连接池汇集,close调用只将对象返回到池而不是物理关闭资源。

请注意 jdbcTemplate 会自动关闭 csCreator csCallback ,但您必须始终关注 ResultSet 。请记住在 finally 块中关闭它们,以确保在发生异常时,将调用 close()

例如,您当前的陈述:

ResultSet rs = null;
try {
    cs.execute();
    ret = obtainObjectX(cs, rs, objectPos, type);
} catch (SQLException e) {
    e.printStackTrace();
}

应替换为:

ResultSet rs = null;
try {
    cs.execute();
    ret = obtainObjectX(cs, rs, objectPos, type);
} catch (SQLException e) {
    e.printStackTrace();
} finally{
    if(rs != null)
        rs.close()
}

<强>更新 来自oracle文档:

  

默认情况下,不启用自动索引。对于JDBC应用程序,如果可以通过getArray和getResultSet方法随机访问数组元素,则为ARRAY对象启用自动索引。

所以这可能会减少数据库上的开放游标:

array.setAutoIndexing(true);

顺便说一下,不推荐使用 oracle.sql.ARRAY 类:

  

已过时。使用java.sql.Array接口进行声明,而不是使用具体的类oracle.sql.ARRAY。