JDBC流式查询需关闭自动提交、设置fetchSize、声明只读前向游标;Excel导出用SXSSF并定期dispose;Oracle时间字段用LocalDateTime等显式解析;优先用临时表分页避免长事务锁表。

用 JDBC 流式查询避免 OOM
直接 Statement.executeQuery() 拿全部结果集会把千万行全加载进内存,Java 进程很快 OOM。必须用流式(streaming)方式逐批拉取,关键在设置 setFetchSize() 和关闭自动提交。
-
connection.setAutoCommit(false)必须关掉,否则 Oracle JDBC 驱动可能忽略 fetch size -
statement.setFetchSize(5000)设为 2000–10000 之间较稳,太小增加网络往返,太大仍占内存 - 用
ResultSet.TYPE_FORWARD_ONLY+ResultSet.CONCUR_READ_ONLY显式声明游标类型,驱动才真正启用服务器端游标 - 别调
resultSet.last()或getRowCount(),会触发全量拉取
选 Apache POI SXSSF 而非 XSSF
千万行用 XSSFWorkbook 写 Excel 等于把整个 .xlsx 结构存在内存,必然崩。必须用 SXSSFWorkbook,它底层用临时文件缓存行,只在内存保最近 rowAccessWindowSize 行。
- 初始化时传
new SXSSFWorkbook(1000),窗口设为 1000 行,比默认 100 更省 GC 压力 - 每写满 10 万行主动调一次
workbook.dispose(),清理已刷盘的临时文件,防磁盘爆满 - 别反复调
sheet.createRow(i)跳着写,SXSSF 对稀疏行支持差,容易撑爆临时文件目录 - 列宽、样式等尽量复用
CellStyle对象,避免重复创建
绕过 Oracle DATE/TIMESTAMP 时区陷阱
Oracle 的 TIMESTAMP WITH TIME ZONE 字段用 rs.getTimestamp() 直接读,JDBC 默认按 JVM 时区转,导出到 Excel 后时间错乱,尤其跨时区部署时。
你是 TypeScript 项目架构专家,专注于脚手架搭建生产就绪的 Node.js 和前端应用。生成包含现代工具链(pnpm、Vite、Next.js)、类型
- 统一用
rs.getObject(colIndex, LocalDateTime.class)或ZonedDateTime.class,显式控制解析逻辑 - 若字段是
DATE类型(无时分秒),用rs.getObject(colIndex, LocalDate.class),避免被自动补零点再转时区 - Excel 单元格格式设为文本或自定义
yyyy-mm-dd,别依赖 Excel 自动识别,否则打开时可能二次转换
用临时表 + 分页减少长事务和锁等待
直接 SELECT * FROM huge_table 扫全表,Oracle 可能持锁太久,阻塞业务写入,且执行计划易走错。
- 先导出条件生成临时表:
CREATE GLOBAL TEMPORARY TABLE tmp_export AS SELECT ... WHERE ...,再从 tmp 表导出,避免长 SQL 锁主表 - 真要分页查,别用
OFFSET/LIMIT(Oracle 12c+ 支持但性能差),改用ROWNUM嵌套或FETCH FIRST+OFFSET组合,且确保排序字段有索引 - 每次分页查完立刻
rs.close()和statement.close(),释放服务器端游标资源,否则 Oracle 会累积大量 OPEN CURSOR
实际跑千万级时,最常卡住的不是代码逻辑,而是临时文件目录空间不足、JVM 堆外内存被 POI 的 TempFile 占满、或者 Oracle 端游标没及时释放——这些得盯着 /tmp、Native Memory Tracking 和 v$open_cursor 看。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










