JFinal3.1 DbPro PreparedStatement Oracle数据库执行异常时会导致ORA-01000: 超出打开游标的最大数

Oracle数据库,在循环中操作大量SQL语句时,如果发生了异常,会导致直到连接关闭时,才能释放游标。这样如果循环次数较多时,就会出现以下错误,导致执行失败:

java.sql.SQLException: ORA-00604: 递归 SQL 级别 1 出现错误

      ORA-01000: 超出打开游标的最大数

      ORA-00604: 递归 SQL 级别 1 出现错误

      ORA-01000: 超出打开游标的最大数

      ORA-01000: 超出打开游标的最大数

DbPro.java中,比如query方法,pst创建执行后,无异常可以正确关闭,但如果DbKit.close(rs,pst)之前出现了异常,则无法立即关闭,需等到conn关闭后才能关闭。 

  <T> List<T> query(Config config, Connection conn, String sql, Object... paras)
      throws SQLException {
    List result = new ArrayList();
    PreparedStatement pst = conn.prepareStatement(sql);
    config.dialect.fillStatement(pst, paras);
    ResultSet rs = pst.executeQuery();
    int colAmount = rs.getMetaData().getColumnCount();
    if (colAmount > 1) {
      while (rs.next()) {
        Object[] temp = new Object[colAmount];
        for (int i = 0; i < colAmount; i++) {
          temp[i] = rs.getObject(i + 1);
        }
        result.add(temp);
      }
    }
    else if (colAmount == 1) {
      while (rs.next()) {
        result.add(rs.getObject(1));
      }
    }
    DbKit.close(rs, pst);
    return result;
  }

如下修改后,解决此异常:

  <T> List<T> query(Config config, Connection conn, String sql, Object... paras)
      throws SQLException {
    List result = new ArrayList();
    PreparedStatement pst = null;
    ResultSet rs = null;
    try {
      pst = conn.prepareStatement(sql);
      config.dialect.fillStatement(pst, paras);
      rs = pst.executeQuery();
      int colAmount = rs.getMetaData().getColumnCount();
      if (colAmount > 1) {
        while (rs.next()) {
          Object[] temp = new Object[colAmount];
          for (int i = 0; i < colAmount; i++) {
            temp[i] = rs.getObject(i + 1);
          }
          result.add(temp);
        }
      }
      else if (colAmount == 1) {
        while (rs.next()) {
          result.add(rs.getObject(1));
        }
      }
    }
    finally {
      DbKit.close(rs, pst);
    }
    return result;
  }


这个类中,有多个方法存在此问题。

评论区

kingyl007

2017-07-29 17:00

本来想把修改后的整个类上传,提示内容超长了

JFinal

2017-07-29 22:36

这个反馈是第一次收到,以前还有没有碰到问题,先做个备忘,回头我仔细分析一下,感谢反馈

HeLei

2019-06-13 17:27

@JFinal 麻烦问一下波总 这个问题现在解决了没有? 或者我们代码要规避这个问题 我用的Jfinal3.5也有这个问题

JFinal

2019-06-13 20:42

@HeLei 这个 query 方法在碰到异常后,会向上抛出,随后 connection.close() 会被调用,那么 ResultSet 与 PreparedStatement 都会被关闭

已然是出现异常了,最终也被关闭了,也就不需要处理了

JFinal

2019-06-13 20:43

@HeLei 当你的代码没出异常时,啥问题也没有

当你的代码出现异常时,在里面关闭 ResultSet 与 PreparedStatement 与在外层的 connection.close() 去关闭这两个对象没有本质区别,反正异常已然发生了

HeLei

2019-06-17 11:05

@JFinal 我们现在的情况是我们在一个方法里面做了一个统一对接接口存储数据,对接时经常因为数据格式不正确导致报错 报错次数过多就回出现这个问题

JFinal

2019-06-17 11:31

@HeLei 只要是报错了,整个过程就是失败的,所以这里的关键就转向于去解决异常,而不是改里的代码了

kingyl007

2019-07-03 15:41

我的业务逻辑是导入操作,在导入之前进行了一些存在性、重复性的校验,这个时候有的报错了,可能是校验出错了,我还是要把剩余的数据检查完之后才统一报错,但如果不改这里,就会出现几个错误就直接返回了,没办法把所有的错误都校验出来。所以我认为还是有必要修改的。

JFinal

2019-07-03 17:29

@kingyl007 这个方法在 jfinal 4.3 中已被改成了 protected,也就是说你可以自己很容易去扩展,而不用对 jfinal 源码进行修改主来满足你的场景,大致方法如下:
public class MyDbPro extends DbPro {
public MyDbPro(String configName) {
super(configName);
}

public List find(String sql, Object... paras) {
System.out.println("Sql: " + sql);
System.out.println("Paras: " + Arrays.toString(paras));
return super.find(sql, paras);
}
}

最后配置一下:
ActiveRecordPlugin arp = new ActiveRecordPlugin(...);
arp.setDbProFactory(configName -> new MyDbPro(configName));

JFinal

2019-07-03 17:30

建议升级到 jfinal 4.3,通过扩展 DbPro 的方式来实现这个功能

更详细的代码示例放在文档中了:
https://www.jfinal.com/doc/5-5

热门反馈

扫码入社