Search code examples
javastored-procedurescursorsqlexception

Java.sql.SQLException: ORA-08103: object no longer exists cursor in stored procedure


Hello I have a method that calls a stored procedure, but I'm getting the next error:

"GRAVE [cl.auter.sictrav.daos.sictrav.ControladorDao] (MSC service thread 1-1) java.sql.SQLException: ORA-08103: object no longer exists : java.sql.SQLException: ORA-08103: object no longer exists"

referring to this line ResultSet rs = (ResultSet)cStmt.getObject(3);

My code is

    public List obtenerBitsEstadoDao(Controlador controlador) {
    DBSictrav conn = new DBSictrav();
    List bitsestado = new ArrayList();
    Connection connection = null;
    try {
        connection = conn.getConection("");
        String query = "{ call pkg_plan_sic.pg_obt_bit_est_crc_ctl(?,?,?,?,?)}";
        try (CallableStatement cStmt = connection.prepareCall(query)) {
            cStmt.setString(1, "CT");
            cStmt.setInt(2, controlador.getId());
            cStmt.registerOutParameter(3, OracleTypes.CURSOR);
            cStmt.registerOutParameter(4, OracleTypes.NUMBER);
            cStmt.registerOutParameter(5, OracleTypes.VARCHAR);
            cStmt.executeQuery();
            if (cStmt.getInt(4) == 0) {
                try
                { 
                    ResultSet rs = (ResultSet)cStmt.getObject(3);
                    while (rs.next()) {
                        BitEstado bc = new BitEstado(rs.getString("BIT_CRUCE"));
                        bc.setFaseReal(rs.getString("BIT_CONTROLADOR"));
                        bc.setPosicion(rs.getInt("POSICION"));
                        bc.setControlador(controlador);
                        bitsestado.add(bc);
                    }
                } catch (Exception e) {
                    logger.log(Level.SEVERE, e.toString(), e);
                }
            }
            else
            {
                throw new SictravDaoException((String) cStmt.getObject(5), cStmt.getInt(4));
            }
        } catch (Exception e) {
            logger.log(Level.SEVERE, e.toString(), e);
        }
    } catch (Exception e) {
        logger.log(Level.SEVERE, e.toString(), e);
    } finally {

        try {
            connection.close();
        } catch (SQLException ex) {
            Logger.getLogger(PlanDao.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    return bitsestado;
}

The Stored Procedure

procedure pg_obt_bit_crc_ctl(
                       p_tipo      in  varchar2,
                       p_id        in  number,
                       p_cur_lis   out cur_ref,
                       p_cod_err   out number,
                       p_des_err   out varchar2) is


 --
  v_string   varchar2(32000);
  v_ind_whe  boolean := false;
  v_num_lin  varchar2(10);
  --
  cursor bit_por_cr is
    select distinct c.id_cruce,
           t.id_masccontrol,
           16 - instr(dec2bin(bit00), '1') + 1 F1
           c.codigo codigo
      from tbl_mascara_control m,
           tbl_controlador     t,
           tbl_cruce           c
     where m.id_masccontrol = t.id_masccontrol
       and c.id_controlador = t.id_controlador
            and c.id_cruce = p_id;
  --
  cursor bit_por_ct is
         select t.id_controlador id_cruce,
             t.id_masccontrol,
           16 - instr(dec2bin(bit00), '1') + 1 F1,
           16-instr(dec2bin(bit08),'1')+1 RR
           //more similar lines 
        from tbl_mascara_control m,
             tbl_controlador t
       where m.id_masccontrol = t.id_masccontrol
         and t.id_controlador =  p_id;
  --
  v_fase_ini       number;
  v_num_ctl        number;
  v_num_fase       number;
  v_ind_fase_check number;
  posIni           number;
  posFin           number;
  v_idx_pal        number;
  v_cnt            number;
  v_num_paralelo   number;
  --
  begin
    -->Borramos los datos de la tabla temporal orientada a la transaccion...
    commit;
--

if p_tipo = 'CT' then
    --
    for c in bit_por_ct loop

        if (c.f1 is not null) then
          insert into tbl_tmp_bits_cruce(id_cruce, bit_cruce, bit_controlador, posicion) values(c.id_cruce, 'F1', 'F1',  c.F1);
        end if;
        //More similar lines ...

        if (c.PR1 is not null) then
          insert into tbl_tmp_bits_cruce(id_cruce, bit_cruce, bit_controlador, posicion) values(c.id_cruce, 'PR1', 'PR1',  c.PR1);
        end if;

        if (c.PR2 is not null) then
          insert into tbl_tmp_bits_cruce(id_cruce, bit_cruce, bit_controlador, posicion) values(c.id_cruce, 'PR2', 'PR2',  c.PR2);
        end if;

        if (c.PR3 is not null) then
          insert into tbl_tmp_bits_cruce(id_cruce, bit_cruce, bit_controlador, posicion) values(c.id_cruce, 'PR3', 'PR3',  c.PR3);
        end if;

        if (c.PR4 is not null) then
          insert into tbl_tmp_bits_cruce(id_cruce, bit_cruce, bit_controlador, posicion) values(c.id_cruce, 'PR4', 'PR4',  c.PR4);
        end if;

        --
        if (c.RR is not null) then
          insert into tbl_tmp_bits_cruce(id_cruce, bit_cruce, bit_controlador, posicion) values(c.id_cruce, 'RR', 'RR',  c.RR);
        end if;
        --
        // More similar ifs
        --
    end loop;
end if;
--
open p_cur_lis for
     select b.id_cruce, b.bit_cruce, b.bit_controlador, b.posicion,c.codigo
     from tbl_tmp_bits_cruce b,tbl_cruce c
     where b.id_cruce=c.id_cruce(+)
     order by b.posicion ;
--
--> ahora recorremos el cursor...
v_num_lin := '60';
p_cod_err := 0;
p_des_err := '';
--
exception
  when others then
      p_cod_err := -20000;
      p_des_err := 'Error al obtener Bits por cruce o controlador';
end pg_obt_bit_crc_ctl;

I'm getting this error and I dont know why or how to fix it. I tried with ojdbc6 and ojdbc14 same result. some suggestion?


Solution

  • The problem is that the cursor returned by your stored procedure is valid only within the same transaction in which the SP itself runs. If your connection is operating in autocommit mode then that transaction ends before the executeQuery() method returns. Out parameters of other types survive the end of the transaction, but the cursor ceases to be valid.

    You can address this problem by taking manual control of the transaction boundaries. To do so, you must ensure that the connection is not in autocommit mode, and you must be certain to manually commit the transaction or roll it back before your method returns. Here's an outline:

    try {
        Connection connection = conn.getConnection();
    
        try {
            CallableStatement cStmt;
    
            connection.setAutoCommit(false);
    
            cStmt = connection.prepareCall(query);
            // ... register parameters ...
    
            try {
                cStmt.executeQuery();
    
                // ... extract and process SP out parameters ...
    
                connection.commit();
            } catch (Exception e) {
                connection.rollback();
                // ... other handling ...
            }
        } finally {
            connection.close();
        }
    } catch (SQLException e) {
        // handle it ...
    }
    

    Also, programming in full defensive mode would call for restoring the original connection autocommit state before returning, so as to avoid interfering with other uses of the underlying Connection managed by your datasource. I have not modeled that above because the datasource should not require it, but this would be a defense against a plausible current or future bug in the DS.