流式逐行读取需设置ResultSet.TYPE_FORWARD_ONLY、CONCUR_READ_ONLY及setFetchSize(Integer.MIN_VALUE),Oracle/MySQL 8+原生支持,PostgreSQL需配合setReadOnly(true);每行处理须在rs.next()循环内完成,避免OOM和游标失效。

直接用流式方式逐行读取,别把整张表加载进内存。
必须设置合适的 ResultSet 类型和并发模式
驱动要支持游标式读取,JDBC 层需显式声明:
-
ResultSet.TYPE_FORWARD_ONLY(单向滚动) -
ResultSet.CONCUR_READ_ONLY(只读) -
statement.setFetchSize(Integer.MIN_VALUE)(启用数据库游标流式拉取)
Oracle 和 MySQL 8+ 都支持该组合,PostgreSQL 需配合 setFetchSize() + setReadOnly(true)。不设 fetchSize 或设为正数,多数驱动仍会缓存一批结果在内存中。
每条记录处理完再取下一条
不要把 ResultSet 存成全局变量或跨方法传递;每一行的数据提取、业务处理、资源释放(如 CLOB 流)必须在 rs.next() 循环体内完成。例如:
try (PreparedStatement ps = conn.prepareStatement(sql,
ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY)) {
ps.setFetchSize(Integer.MIN_VALUE);
try (ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
String id = rs.getString("id");
Clob content = rs.getClob("content");
// ✅ 立即用 getCharacterStream() 读 CLOB,不能先调 length() 或 getSubString()
try (Reader reader = content.getCharacterStream()) {
char[] buf = new char[8192];
int n;
while ((n = reader.read(buf)) != -1) {
processChunk(buf, 0, n);
}
} // 自动关闭 Reader,释放 LOB 句柄
}
}
}连接与事务生命周期要匹配
流式查询期间连接不能提前关闭,否则游标失效、读取中断。
- 不要在事务里做超长时间流式处理(比如导出百万行到文件),容易触发连接池超时或事务超时。
- 若需长耗时处理,建议:开启新连接(非事务)、禁用自动提交、手动控制 close 时机。
- Oracle 特别注意:连接 URL 加
?implicitCachingEnabled=false,避免驱动内部缓存整个 CLOB。
别碰这些高危操作
-
rs.getBlob(...).getBinaryStream()或rs.getClob(...).getSubString(1, (int) clob.length())→ 直接 OOM -
rs.getObject(...)返回 List/Map 大对象 → 本质仍是全量加载 -
rs.toString()或clob.toString()→ 返回的是类名,不是内容 -
BufferedReader.readLine()用于无换行 CLOB → 可能阻塞或读不完 - 在
while(rs.next())外部保存rs.getClob()引用 → 下一行时流已关闭,抛SQLException


















