Oracle CLOB读取应使用getClob()而非getString(),检查null并调用free();写入需setClob()或启用SetBigStringTryClob;批量操作须禁用自动提交;Spring JdbcTemplate需自定义映射。
读取 Oracle CLOB 时 getString() 返回 null 或空字符串
oracle 的 clob 字段不能直接用 resultset.getstring() 安全读取——尤其当内容超过数据库驱动默认缓冲区(如 oracle 12c jdbc 驱动对 getstring() 的隐式截断阈值是 4000 字节),或字段实际为 null 但未显式判断。更关键的是,某些驱动版本在 auto-commit=false 场景下,未调用 free() 可能导致后续读取异常。
- 改用
ResultSet.getClob()获取Clob对象,再调用clob.getSubString(1, (int) clob.length()) - 务必检查
clob是否为null(对应 SQL NULL),避免NullPointerException - 读完后建议显式调用
clob.free()(JDBC 4.0+),防止连接池中 LOB 句柄泄漏 - 若内容超 2GB,
getSubString会抛SQLException;此时需用getCharacterStream()分块读取
写入大文本到 Oracle CLOB 字段失败:ORA-01461 错误
ORA-01461: can bind a LONG value only for insert into a LONG column 是典型错误,本质是 JDBC 驱动无法自动将 Java String 映射为 CLOB,而试图塞进普通 VARCHAR2 绑定槽。常见于使用 PreparedStatement.setString() 写入超长文本。
- 必须用
PreparedStatement.setClob(int, Reader)或setClob(int, Clob),而非setString() - 推荐方式:用
Connection.createClob()创建临时Clob,写入内容后绑定:Clob clob = conn.createClob(); clob.setString(1, largeText); ps.setClob(1, clob);
- 若用
setClob(int, Reader),确保Reader编码与数据库字符集一致(如 AL32UTF8),否则乱码 - Oracle 12.1+ 支持
setString()直接写入 CLOB,但需驱动版本 ≥ 12.1.0.2 且启用SetBigStringTryClob=true连接参数
批量插入含 CLOB 的记录性能极差
每个 CLOB 写入都会触发独立 LOB 定位符分配和数据传输,批量 addBatch() 时默认行为是逐条提交 LOB,导致网络往返激增、事务日志膨胀。
- 禁用自动提交:
conn.setAutoCommit(false),并在 batch 执行后统一commit() - 对每条记录,仍需调用
createClob()+setString()构造Clob,但避免在循环内反复获取Connection - 考虑改用 Oracle 原生批量接口:
OraclePreparedStatement.setARRAY()配合OracleType.CLOB数组(需 ojdbc8+ 和特定配置) - 极端场景(百万级 CLOB 插入)应绕过 JDBC,改用 SQL*Loader 或 DBMS_LOB API 在服务端处理
Spring JdbcTemplate 中处理 CLOB 的坑
JdbcTemplate 默认不支持 CLOB 类型映射,queryForObject(..., String.class) 会静默返回空或截断,且 update() 使用 setString() 绑定必然报 ORA-01461。
- 读取时,必须自定义
RowMapper,用rs.getClob()+clob.getSubString()显式提取 - 写入时,放弃
update(String sql, Object... args)简写,改用带PreparedStatementSetter的重载:jdbcTemplate.update(sql, ps -> { Clob clob = con.createClob(); clob.setString(1, text); ps.setClob(1, clob); }); - 注意
DataSource连接池(如 HikariCP)是否启用了leakDetectionThreshold,未释放Clob会触发连接泄漏告警
真正麻烦的不是语法,是 CLOB 生命周期和连接状态的耦合——一旦在事务外提前 close() 了 Clob,或者连接被池回收而 Clob 还没读完,就会出现难以复现的流关闭异常或空指针。这些细节不会报错在日志第一行,得看堆栈最深处的 oracle.sql.CLOB 调用链。
立即学习“Java免费学习笔记(深入)”;










