必须用preparedstatement.setbinarystream(),禁用setbytes();否则>10mb文件极易触发outofmemoryerror。因setbytes()会将整个byte[]全量加载进jvm堆,而setbinarystream()让数据直通数据库协议层,不经过java堆中转。

必须用 PreparedStatement.setBinaryStream(),禁用 setBytes();否则 >10MB 文件极易触发 OutOfMemoryError。
为什么 setBytes() 会崩内存?
Oracle JDBC 驱动在调用 setBytes() 时,会把整个 byte[] 全量加载进 JVM 堆——哪怕你传的是一个 100MB 的 PDF,JVM 就得立刻分配至少 100MB 连续堆空间。这不是“慢”,是直接 OOM。而 setBinaryStream() 让数据从 InputStream 直通数据库网络协议层,不经过 Java 堆中转。
常见错误现象:
- 上传 20MB 图片时抛
java.lang.OutOfMemoryError: Java heap space - 日志里反复出现 GC overhead limit exceeded
- 连接池里连接频繁断开,伴随
ORA-01002: fetch out of sequence
setBinaryStream() 的三个参数怎么填?
必须用三参数重载:ps.setBinaryStream(paramIndex, inputStream, length)。其中 length 是硬性要求,不能传 -1 或 null —— Oracle 驱动会因此退化为内存缓冲模式,等于白换。
实操要点:
- 若源头是
FileInputStream,用file.length()算准长度 - 若来自 HTTP
Part,优先读取请求头Content-Length;不可信时需先part.getInputStream().readAllBytes()(仅限小文件)再传长度 - Oracle 12c+ 推荐用
int类型的长度参数(即setBinaryStream(int, InputStream, int)),避免旧驱动对long截断 -
InputStream不能提前关闭:它只在executeUpdate()执行时才被真正消费
为什么必须配 FOR UPDATE 和手动事务?
Oracle BLOB 不是字段值,是 LOB locator(定位器)。INSERT 时用 EMPTY_BLOB() 只生成一个空 locator,后续写入必须通过这个 locator 定位并加锁。没 FOR UPDATE,blob.setBinaryStream() 就找不到可写的 LOB 实例,直接报 ORA-22288: file or LOB operation failed 或 row containing the LOB value is not locked。
关键步骤顺序不能错:
-
conn.setAutoCommit(false)必须在 INSERT 前设置 - INSERT 后**立刻**执行
SELECT ... FOR UPDATE,中间不能有其他 SQL 或连接复用 - 从
ResultSet拿到的必须是oracle.sql.BLOB(不是java.sql.Blob),才能调getBinaryOutputStream() - 流写入、
flush()、close()必须在conn.commit()前完成
连接池和驱动版本的隐藏坑
HikariCP、Druid 等连接池默认启用连接复用,但 Oracle LOB 流绑定的是物理连接上下文。如果 OutputStream 关闭前连接被归还,后续 executeUpdate() 可能失败或静默丢数据。
ojdbc 版本影响显著:
- ojdbc6:不支持
InputStream.mark()/reset(),且getBinaryStream()关闭后不自动释放 LOB 句柄 - ojdbc8:修复多数流生命周期问题,但仍要求显式设
setFetchSize(1)防止 ResultSet 默认拉取整行含 BLOB - URL 参数建议加上
?oracle.jdbc.streamChunkSize=32768,让驱动分块传输更稳定
最易被忽略的一点:Spring @Transactional 方法里分两步操作(先 insert 再 select for update)看似合理,但如果中间调用了非事务方法、或事务传播被意外覆盖,locator 就失效——这个失败不报错,只在写入时静默失败或抛 ORA-22288,排查成本极高。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











