设置大fetchSize查询Oracle触发ArrayIndexOutOfBoundsException求助
问题描述
使用JDBC查询Oracle数据库时,设置fetchSize为10执行正常,但设置为100000时抛出ArrayIndexOutOfBoundsException,错误栈指向Oracle驱动的T4CNumberAccessor.unmarshalOneRow方法。相关代码如下:
ParsedSql parsedSql = NamedParameterUtils.parseSqlStatement(apiSql); MapSqlParameterSource paramSource = new MapSqlParameterSource(param); String sqlToUse = NamedParameterUtils.substituteNamedParameters(parsedSql, paramSource); List<SqlParameter> declaredParameters = NamedParameterUtils.buildSqlParameterList(parsedSql, paramSource); PreparedStatementCreatorFactory creatorFactory = new PreparedStatementCreatorFactory(sqlToUse, declaredParameters); Object[] params = NamedParameterUtils.buildValueArray(parsedSql, paramSource, null); PreparedStatementCreator creator = creatorFactory.newPreparedStatementCreator(params); PreparedStatement preparedStatement = creator.createPreparedStatement(conn); if(batchCount > maxBatchCount || batchCount == 0){ batchCount = (int)maxBatchCount; } preparedStatement.setFetchSize(batchCount); ResultSet resultSet = preparedStatement.executeQuery();
错误信息:
java.sql.SQLException: Error at com.alibaba.druid.pool.DruidDataSource.handleConnectionException(DruidDataSource.java:1770) at com.alibaba.druid.pool.DruidPooledConnection.handleException(DruidPooledConnection.java:133) at com.alibaba.druid.pool.DruidPooledStatement.checkException(DruidPooledStatement.java:82) at com.alibaba.druid.pool.DruidPooledPreparedStatement.executeQuery(DruidPooledPreparedStatement.java:240) ... Caused by: java.lang.ArrayIndexOutOfBoundsException: 802200000 at oracle.jdbc.driver.T4CNumberAccessor.unmarshalOneRow(T4CNumberAccessor.java:201) at oracle.jdbc.driver.T4CTTIrxd.unmarshal(T4CTTIrxd.java:945) ...
环境信息:
- JDK 8
- ojdbc-11.2.0.4
- Oracle 10/11/12
- 执行的是包含89个字段的全量查询SQL
原因分析
这个错误是Oracle JDBC驱动内部的缓冲区计算缺陷导致的:
- 当
fetchSize设置过大(如100000),同时查询结果包含大量字段(89个)时,驱动为数值类型字段分配解析缓冲区时,计算的数组大小超出了内部限制,触发数组越界。 - ojdbc-11.2.0.4属于较老版本,针对大fetchSize+多字段的场景存在未修复的bug,尤其是在处理
NUMBER类型字段的批量解析时。
解决方案
降低fetchSize到合理范围
不要设置过大的fetchSize,建议调整为1000-5000之间。过大的fetchSize不仅会触发驱动bug,还会占用大量内存、增加GC压力,反而降低整体性能。升级JDBC驱动版本
更换为适配JDK8的更高版本驱动,比如ojdbc8(兼容Oracle 10/11/12)或12.2.0.1及以上的ojdbc版本。新版本驱动修复了大量缓冲区计算和数据解析的问题,能更好地处理大fetchSize场景。检查并优化字段类型
如果查询结果中存在高精度NUMBER类型字段(如NUMBER(38)),在业务允许的前提下,可以调整字段精度,或者在查询时显式转换为合适的类型,减少驱动解析时的缓冲区压力。分页替代全量查询
即使需要获取全量数据,也可以通过分页查询分批处理,比如使用Oracle的ROWNUM或FETCH NEXT ... ROWS ONLY语法,分批次读取数据,避免一次性设置过大的fetchSize。
内容的提问来源于stack exchange,提问作者Ivan
相关产品推荐
相关产品推荐

