Oracle JDBC 的 setFetchSize() 默认不生效,因驱动默认忽略该设置,须显式启用行预取(如设 oracle.jdbc.defaultRowPrefetch 或 executeQuery 前调用 setFetchSize),且需注意复用 PreparedStatement 时的残留影响、LOB 字段降级为 1、HikariCP/Druid 兼容性及仅对读操作有效等关键限制。
oracle jdbc 的 setfetchsize() 默认几乎不生效,不是你代码写错了,而是驱动默认忽略该设置——必须显式启用行预取机制,否则每次仍只拉 10 行,网络往返爆炸式增长。
为什么 setFetchSize() 在 Oracle 里经常白设
Oracle 驱动默认使用「行缓冲」模式,且服务端会无视客户端的 fetch size 请求。它真正认的是连接级参数 oracle.jdbc.defaultRowPrefetch,或在 executeQuery() 前对 PreparedStatement 显式调用 setFetchSize()。更隐蔽的问题是:一旦 PreparedStatement 被复用(比如循环查不同 SQL),上次设置的 fetch size 可能残留并干扰当前查询。
-
setFetchSize()必须在executeQuery()之前调用,之后调用完全无效 - 含
LONG、XMLTYPE或大BLOB字段时,fetch size 会被自动降为 1,需额外加oracle.jdbc.useFetchSizeWithLongColumn=true - HikariCP 通常保留 Statement 属性,但老版本 Druid 可能重置
setFetchSize()设置
怎么设才真正起作用(以 PreparedStatement 为例)
别信“设一次全局生效”的说法。最稳妥的方式是在每次获取 PreparedStatement 后、执行前立即设值,并绑定具体业务场景:
- 导出百万订单:设
ps.setFetchSize(500),平衡网络包大小与 JVM 堆压力 - 字段含
CLOB且必须流式读取:URL 加?oracle.jdbc.useFetchSizeWithLongColumn=true&oracle.jdbc.defaultRowPrefetch=200 - 用
JdbcTemplate:调jdbcTemplate.setFetchSize(500),但它只影响后续创建的Statement,不改变已存在的
FetchSize 设多大才合适
不是越大越好。设成 10000 可能比 100 还慢,因为:
- TCP 包超 MTU 触发分片,丢包重传概率上升
- JVM 一次性分配大数组,GC 暂停时间陡增(尤其 G1 垃圾回收器)
- Oracle 服务端 PGA 内存占用飙升,DBA 可能 kill 掉会话
- 实测中,500–2000 是多数 OLAP 导出场景的甜点区间;若网络延迟高(如跨机房),优先选 200–500
如何验证它真生效了
别靠猜,抓包或日志才是唯一依据:
立即学习“Java免费学习笔记(深入)”;
- 启动时加 JVM 参数:
-Doracle.jdbc.Trace=true,grep 日志里的fetchSize和rowsFetched - 用 Wireshark 抓包,过滤
tcp.port == 1521,看单次响应 payload 是否接近你设的 fetch size × 平均行宽 - 查 Oracle 动态视图:
SELECT sql_id, fetches, rows_processed FROM v$sql WHERE sql_text LIKE '%your_query%',确认fetches明显下降
最容易被忽略的点:setFetchSize() 对批量写入(executeBatch())完全无效,它只管读;而含 LOB 字段时,即使 URL 里开了 useFetchSizeWithLongColumn=true,也得确保字段是通过 rs.getCharacterStream() 或 rs.getBinaryStream() 流式读取,否则仍会触发整列加载。



















