不能用 getString() 读取 Oracle 的 LONG 列,因自12c起JDBC驱动默认禁用其直接映射,会报“Invalid column type”或截断;必须用 getCharacterStream() 流式读取以完整获取最大2GB内容。
直接用 resultset.getstring() 读取 oracle 的 long 列,在 19c 及较新 jdbc 驱动(ojdbc8+)下大概率会抛 java.sql.sqlexception: invalid column type 或静默截断——这不是 bug,是 oracle 明确弃用 long 后的兼容性限制。必须改用流式读取。
为什么不能用 getString() 读取 LONG
Oracle 自 8i 起就标记 LONG 为“过时类型”,12c 后 JDBC 驱动默认禁用其直接映射。驱动会把 LONG 列识别为 OTHER 或 LONGVARBINARY 类型,getString() 拒绝处理非字符类型列。即使侥幸返回字符串,长度也常被截断到 4000 字节以内。
getCharacterStream() 是最稳妥的替代方案
它绕过类型映射,让 JDBC 驱动以字符流方式拉取数据,底层实际调用 Oracle 的 LOB 流接口,能完整读取最大 2GB 的 LONG 内容。
-
ResultSet.getCharacterStream("col_name")返回java.io.Reader,需手动读取至String - 必须在同一次
ResultSet迭代中完成读取,流关闭后无法重读 - 不支持随机访问或
mark()/reset(),只能顺序读到底 - 若列值为
NULL,返回null,需判空
实际读取代码要防阻塞和内存溢出
别用 readLine() 循环——遇到换行符就停,会丢内容。用固定缓冲区 + StringBuilder 才可靠:
Reader reader = rs.getCharacterStream("long_col");
if (reader != null) {
StringBuilder sb = new StringBuilder();
char[] buf = new char[4096];
int len;
while ((len = reader.read(buf)) != -1) {
sb.append(buf, 0, len);
}
String result = sb.toString();
}
- 缓冲区大小选
4096是平衡 IO 次数与堆内存占用的常见值 - 避免用
StringBuffer(同步开销无必要),单线程场景StringBuilder足够 - 别漏掉
reader.close()—— 虽然ResultSet.close()通常会级联关闭,但显式关闭更安全
MyBatis 中怎么配
MyBatis 默认不支持 LONG,需显式指定 jdbcType 并配合自定义 TypeHandler:
- XML 映射里写:
- 但
LONGVARCHAR在 ojdbc8 下仍可能失败,更稳的是用jdbcType="OTHER"+ 自定义 handler - handler 中复用上面的
getCharacterStream()逻辑,而不是依赖rs.getString() - 注意:MyBatis 的
@Select注解方法无法指定jdbcType,必须用 XML 或@Results
真正麻烦的不是读取动作本身,而是整个链路——从 Oracle 服务端返回流、JDBC 驱动解析、Java 层缓冲区管理,任一环节超时或中断都会导致数据不全。生产环境务必加超时控制和日志记录原始字节数,否则出问题时连是不是读全了都难验证。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











