Java中JDBC处理BLOB需流式读写防OOM:读用getBinaryStream配BufferedInputStream分块,禁用getBytes();写用setBinaryStream或Oracle两步法,配合事务与连接参数调优。

Java 中用 JDBC 处理数据库 BLOB 字段,核心是避免把整个二进制内容一次性加载进内存,否则极易触发 OutOfMemoryError。关键不在于“能不能读”,而在于“怎么流式读、安全写”。
读取 BLOB 要用流,别碰 getBytes() 和 getString()
ResultSet 中的 BLOB 字段必须通过流接口获取,而不是全量方法:
- 用
rs.getBinaryStream("column_name")获取InputStream,配合BufferedInputStream分块读(如每次 8KB) - 绝对禁用
rs.getBlob("col").getBytes()或rs.getString("col")—— 这些会把全部数据塞进 JVM 堆 - 调用
getBinaryStream()后,该列不能再调其他 getter(比如再调getString()),否则抛异常 - 流必须显式关闭(推荐
try-with-resources),否则连接可能被长期占用
写入 BLOB 要分步或用流,别构造大 byte[]
插入或更新 BLOB 数据时,不能先读文件到 byte[] 再传入,应直接使用流:
- MySQL:用
ps.setBinaryStream(1, inputStream, -1),长度传-1让驱动自动探测 - Oracle:必须两步走——先
INSERT ... VALUES (empty_blob()),再SELECT ... FOR UPDATE拿到oracle.sql.BLOB,调getBinaryOutputStream()写入 - 无论哪种数据库,都需确保
connection.setAutoCommit(false),尤其 Oracle 写入必须事务控制 - MySQL 需检查服务端参数
max_allowed_packet,确保大于待写入数据,否则报 “Packet too large”
连接和查询设置影响流行为
很多 OOM 问题其实出在 JDBC 驱动默认缓存整行数据,必须主动干预:
- 执行查询前设
statement.setFetchSize(Integer.MIN_VALUE)(MySQL Connector/J 支持流式游标) - MySQL 连接 URL 加参数:
?useServerPrepStmts=false&cachePrepStmts=false&tinyInt1isBit=false - Oracle ojdbc8 推荐加:
;defaultRowPrefetch=1;oracle.jdbc.defaultLobPrefetchSize=0 - 连接池(如 HikariCP)中,流关闭时机要小心:Oracle 的
OutputStream必须在executeUpdate()返回后才关
不同数据库的典型差异
不能一套代码打天下,BLOB 行为高度依赖数据库类型和驱动版本:
- MySQL 8.0+ 驱动默认支持流式结果,但需配
setFetchSize(Integer.MIN_VALUE);5.1.x 则需 URL 加&useCursorFetch=true&defaultFetchSize=1 - Oracle 不允许对未初始化字段直写,
empty_blob()+FOR UPDATE是强制流程;ojdbc6 对流不支持mark()/reset(),ojdbc8 改进较多但仍需注意 LOB 句柄释放 - BLOB 字段读取必须在当前 ResultSet 行内完成,调
rs.next()后前一行的流立即失效
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











