必须升级Oracle JDBC驱动至ojdbc8.jar并启用流式写入,否则大于4GB的BLOB会报ORA-01461;正确做法是先插入EMPTY_BLOB()占位,再通过getBlob().setBinaryStream(0)分块写入,全程手动控制事务超时与校验长度。

Oracle JDBC驱动必须用12c+且启用流式写入
旧版驱动(如11g jdbc driver)对大于4GB的BLOB会直接抛 SQLException: ORA-01461,不是你代码的问题,是驱动底层没实现分块流写。必须升级到 ojdbc8.jar(对应Oracle 12cR2+),并在连接URL里显式开启流支持:jdbc:oracle:thin:@//host:1521/orcl?useUnicode=true&characterEncoding=UTF-8&oracle.jdbc.streams=true。漏掉 oracle.jdbc.streams=true 这个参数,即使驱动新版,setBinaryStream() 仍可能退化成内存全载入。
不能用 setBytes() 或 setBlob(new SerialBlob())
这两个方法会把整个文件一次性读进JVM堆内存,1GB文件就可能触发 OutOfMemoryError,且Oracle服务端也会因单次绑定过大而拒绝。正确路径是:先插入空BLOB占位,再通过 getBLOB().setBinaryStream(0) 获取输出流,边读边写。
关键步骤:
- 用
PreparedStatement插入带EMPTY_BLOB()的记录,例如:INSERT INTO doc (id, content) VALUES (?, EMPTY_BLOB()) -
executeUpdate()后立即调用getGeneratedKeys()或重新SELECT ... FOR UPDATE锁定该行 - 调用
resultSet.getBlob("content").setBinaryStream(0)获取可写流 - 用
Files.newInputStream()打开源文件,每次read()≤ 8192 字节写入BLOB流
事务必须手动控制且不能超时
BLOB写入过程可能持续数分钟,Oracle默认事务超时(SQLNET.EXPIRE_TIME 或应用服务器连接池配置)会导致连接中断、BLOB残留为临时段。必须:
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
立即学习“Java免费学习笔记(深入)”;
- 禁用连接池的自动提交(
setAutoCommit(false)) - 在写入前调用
connection.setNetworkTimeout(...)设置足够长的超时(如 30 分钟) - 写完后显式
commit(),失败则rollback()—— 不要依赖 try-with-resources 自动关闭,它不保证事务边界
特别注意:如果用 Spring @Transactional,需设 timeout = 1800 并确认传播行为是 REQUIRED,否则嵌套调用可能意外提交中间状态。
写入完成后务必检查BLOB长度是否匹配
网络中断或流未正确 close() 可能导致BLOB实际长度小于源文件,但无异常抛出。写完后应立刻执行:
SELECT DBMS_LOB.GETLENGTH(content) FROM doc WHERE id = ?
与源文件 Files.size(path) 比对。不一致说明写入不完整,此时BLOB已持久化但损坏,只能删掉重来。别跳过这步校验——这是唯一能发现静默截断的方式。

















