Oracle JDBC的fetchSize常不生效,根本原因是驱动默认忽略setFetchSize(),仅当满足三个条件时才启用服务器端分批拉取:必须使用PreparedStatement、ResultSet.TYPE_FORWARD_ONLY和CONCUR_READ_ONLY;且必须显式调用connection.setAutoCommit(false),否则报ORA-01002或退化为全量加载;fetchSize须在executeQuery()前设置,对含CLOB/LONG字段的查询需额外启用useFetchSizeWithLongColumn=true。
Oracle JDBC的fetchSize为什么经常不生效
根本原因不是代码写错了,而是oracle驱动默认忽略客户端传来的setfetchsize()——它只在满足三个硬性条件时才启用服务器端分批拉取:必须用preparedstatement(不能是statement),resultset.type_forward_only,且resultset.concur_read_only。缺一不可。更关键的是,连接必须处于手动事务模式:connection.setautocommit(false)。否则即使设了1000,执行时仍报ora-01002: fetch out of sequence,或干脆退化成一次性加载全量。
设置fetchSize的正确时机和位置
必须在调用executeQuery()之前完成设置,之后调用setFetchSize()完全无效。而且这个值只对当前PreparedStatement实例生效,不能复用后还指望它保持上次的值。
- 优先在
prepareStatement()后立即调用setFetchSize(500) - 如果连接来自HikariCP等主流池,确认池未重置
Statement属性(Hikari默认不会,但老版Druid可能) - 避免在同一个
PreparedStatement上反复执行不同SQL——前一次的fetchSize会残留并干扰下一次 - 连接URL中加
?oracle.jdbc.defaultRowPrefetch=500可全局兜底,但失去动态调整能力
含CLOB/LONG字段时fetchSize被静默降级为1
Oracle驱动遇到CLOB、LONG、XMLTYPE等大对象类型时,会自动把fetchSize强制设为1,防止内存爆炸。这不是bug,是保护机制。
- 若必须启用流式读取,需在连接URL中显式加
&oracle.jdbc.useFetchSizeWithLongColumn=true - 同时确保应用层用
rs.getClob("col")获取句柄,再调用clob.getSubString()或流式读取,而非直接rs.getString() - 验证是否生效:开启JDBC日志(
oracle.jdbc.Trace=true),搜索rowsFetched,看每次next()是否真按预期批量返回
fetchSize设太大反而拖慢查询
设成10000不等于更快。Oracle客户端一次拉取的数据先缓存在JVM堆里,再逐行交给应用。过大会触发TCP分片、GC压力陡增、服务端PGA内存超限——DBA可能直接杀掉会话。
- 建议从
100起步,逐步试到500~2000,观察网络延迟与GC日志变化 - 注意:真正开始拉数据是在第一次
rs.next(),不是executeQuery();所以next()耗时长,说明fetchSize没起作用或网络/服务端瓶颈 - 务必在流式读完后调用
connection.close()——Oracle服务端游标不会自动释放,不关连接会导致ORA-01000



















