oracle 19c json列在jdbc中必须用rs.getobject("j", string.class)配合sqltype.json(ojdbc8u3+)才能正确读取字符串,否则报错或乱码;需确保服务端字符集为al32utf8且处理null。
oracle 19c 的 json 列在 jdbc 中不能直接当字符串读
java 应用连 oracle 19c 时,如果表里用了原生 json 数据类型(比如 create table t (j json)),jdbc 驱动默认不会把它转成 string 或 jsonobject。你用 rs.getstring("j") 很可能抛 sqlexception: cannot convert to string,或者返回一堆乱码二进制内容。
根本原因是:Oracle JDBC 驱动(19c 对应 ojdbc8)把 JSON 当作独立 SQL 类型处理,底层映射到 oracle.sql.json.OracleJsonStructure,不是通用类型。
- 必须显式注册
SQLType.JSON才能正确获取——不是靠驱动自动推断 - 低版本驱动(如 ojdbc7)压根不认
JSON类型,会报ORA-00902: invalid datatype - 别试
rs.getObject("j", String.class),它会 fallback 到默认行为,大概率失败
用 rs.getObject(int, Class) + SQLType.JSON 拿原始 JSON 字符串
最轻量、兼容性最好的方式是让 JDBC 直接吐出标准 JSON 字符串,后续交给 Jackson/Gson 解析。关键点在于:必须用带类型参数的 getObject,且传 java.sql.SQLType.JSON(注意不是字符串名)。
示例代码片段:
String sql = "SELECT j FROM my_table WHERE id = ?";
try (PreparedStatement ps = conn.prepareStatement(sql);
ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
// ✅ 正确:指定 SQLType.JSON,驱动返回 String
String jsonStr = rs.getObject("j", String.class);
// 或用列索引:rs.getObject(1, String.class)
<pre class="brush:php;toolbar:false;"> // ❌ 错误:rs.getString("j") 可能失败
// ❌ 错误:rs.getObject("j") 返回 OracleJsonStructure,不能直接 toString()
}}
- 驱动版本必须 ≥ ojdbc8u3(即 19.19+),旧版不支持
SQLType.JSON -
String.class是唯一被 JDBC 驱动原生支持的 target type;传JsonObject.class会报SQLFeatureNotSupportedException - 如果字段允许为 NULL,
rs.getObject("j", String.class)返回null,不是空字符串
想直接映射成 Java 对象?得自己写 ResultSetExtractor 或用 MyBatis TypeHandler
JDBC 层面没有内置 JSON → POJO 的转换,Spring JDBC 的 BeanPropertyRowMapper 和 MyBatis 默认规则都识别不了 JSON 列。必须手动介入解析流程。
以 Spring JDBC 为例,推荐用 ResultSetExtractor 显式控制:
return jdbcTemplate.query(sql, new ResultSetExtractor<list>>() {
@Override
public List<mypojo> extractData(ResultSet rs) throws SQLException {
List<mypojo> list = new ArrayList();
while (rs.next()) {
MyPojo p = new MyPojo();
p.setId(rs.getLong("id"));
String jsonStr = rs.getObject("j", String.class); // 先取字符串
if (jsonStr != null) {
p.setDetail(objectMapper.readValue(jsonStr, Detail.class));
}
list.add(p);
}
return list;
}
});</mypojo></mypojo></list>
- 别在
@Column上加@Convert或 JPA 的@Convert,JPA 3.1 之前根本不支持JSON类型 - MyBatis 需自定义
TypeHandler<detail></detail>,在getResult(ResultSet, String)里调rs.getObject(col, String.class)再反序列化 - 所有 JSON 解析逻辑必须放在应用层,数据库不参与对象构造
性能和字符集坑:确保 NLS_CHARACTERSET 是 AL32UTF8
即使代码全对,Oracle 服务端字符集设成 ZHS16GBK 或 WE8ISO8859P1 会导致 rs.getObject("j", String.class) 返回乱码或截断,尤其含中文、emoji 时。
- 查当前字符集:
SELECT * FROM NLS_DATABASE_PARAMETERS WHERE PARAMETER = 'NLS_CHARACTERSET'; - AL32UTF8 是唯一安全选项;其他字符集下 JSON 字符串可能被错误编码,驱动无法修复
- 连接 URL 加
?useUnicode=true&characterEncoding=utf8没用——这是 MySQL 的参数,Oracle JDBC 忽略它 - 如果改库字符集不可行,只能在应用层对拿到的
String做new String(bytes, StandardCharsets.UTF_8)补救(但极不可靠)
真正麻烦的从来不是怎么写那几行代码,而是确认驱动版本、服务端字符集、NULL 处理这三件事有没有漏掉一个。少一个,JSON 就会以意想不到的方式消失。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










