Oracle 19c JSON列在JDBC中默认映射为String,需手动用Jackson解析;写入须用writeValueAsString生成合规JSON,大JSON用setClob;路径查询不可参数化,索引依赖虚拟列或严格匹配的JSON_EXISTS路径。
String
oracle 19c 原生支持 json 类型(底层是 varchar2/clob + 检查约束),但 jdbc 驱动(如 ojdbc8.jar 21.9+)并不会自动将其转为 java 对象。你从 resultset 中取值时,rs.getobject("col") 或 rs.getstring("col") 返回的仍是原始 json 字符串,不是 jsonobject 或 map。
这意味着:不能直接调用 getJSONObject();也不能指望 Hibernate/JPA 自动解析(除非手动配 @Convert)。
实操建议:
rs.getString("json_col") 获取字符串,再交由 Jackson/Gson 解析(推荐 Jackson 的 ObjectMapper.readTree(),支持 JsonNode 随机访问)NULL,注意 rs.wasNull() 判断,避免传 null 给 ObjectMapper
rs.getBlob() 或 rs.getClob() 直接读 —— Oracle 的 JSON 列不返回 CLOB 实例,强转会抛 ClassCastException
Oracle 要求写入 JSON 列的值必须通过 JSON 构造函数或满足语法校验,否则报 ORA-40442: JSON syntax error。JDBC 层无法自动加引号或转义,全靠你控制。
常见错误现象:
立即学习“Java免费学习笔记(深入)”;
"{'name':'" + name + "'}" → 单引号、换行、反斜杠未转义,入库后 IS JSON 检查失败PreparedStatement.setString() 写入合法 JSON 字符串,但 Oracle 仍报 ORA-40495: JSON value too large(实际是 CLOB 长度超限,而非内容非法)实操建议:
objectMapper.writeValueAsString(obj) 生成严格合规的 JSON 字符串setClob() + StringReader,避免驱动内部截断(ojdbc8 默认将长字符串当 VARCHAR2 处理)SELECT :json_str IS JSON FROM DUAL(绑定参数),返回 1 表示合法$. 语法)Oracle 支持 JSON_VALUE、JSON_QUERY、JSON_EXISTS 等函数,但路径表达式(如 $.address.city)必须作为字符串字面量或绑定变量传入 —— 不能拼接,否则 SQL 注入风险极高。
正确写法示例(使用 PreparedStatement):
String sql = "SELECT id, JSON_VALUE(data, '$.user.name') AS name " + "FROM users WHERE JSON_EXISTS(data, '$.user.active ? (@ == true)')";PreparedStatement ps = conn.prepareStatement(sql);// 路径表达式本身不能参数化(Oracle 不支持绑定路径),但谓词值可以// 所以这里 $.user.active 是硬编码,true 是绑定参数ps.setBoolean(1, true);
关键限制:
JSON_VALUE 第二个参数(路径)**不支持绑定变量**,只能写死或动态拼接(需严格白名单校验路径段)field=user.phone),必须拆解并校验每一段是否只含字母、数字、下划线、点,拒绝 $[0].x' OR 1=1 -- 类注入JSON_QUERY 返回 CLOB,Java 中要用 getClob() + getSubString() 读取,别用 getString()
Oracle 对 JSON 字段建索引有两种主流方式:虚拟列 + B-Tree 索引,或原生 JSON_SEARCH + 函数索引。Java 层虽不建索引,但写法直接影响索引能否命中。
容易踩的坑:
JSON_VALUE(data, '$.status') = 'active' → 若没建对应虚拟列索引,全表扫描JSON_EXISTS(data, '$?(@.status == "active")') → 可走函数索引,但 Java 中拼错路径(比如少个 @)会导致索引失效CTXSYS.CONTEXT)后,在 Java 中调用 CONTAINS(),但忘了在 PreparedStatement 中设 setString(1, "{status:active}") 格式必须匹配索引配置实操建议:
WHERE status_gen = ?
JSON_EXISTS,确保路径中所有键名都小写(Oracle 默认大小写敏感),且与建索引时的路径完全一致EXPLAIN PLAN FOR ... 在 Java 中执行一次查询,再查 PLAN_TABLE 确认是否用了 INDEX RANGE SCAN最常被忽略的一点:Oracle 的 JSON 函数索引对空格、换行、重复键等“宽松 JSON”不友好 —— Java 侧生成 JSON 时必须用 objectMapper.enable(SerializationFeature.INDENT_OUTPUT) 关掉格式化,保证输出紧凑无冗余空格。