You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JPA原生查询JSON列时动态指定键名与值的问题排查及解决

JPA原生查询JSON列时动态指定键名与值的问题排查及解决

刚好碰到和你一样的需求场景:用JPA原生查询操作存储JSON字符串的数据库列,还要能动态指定要查询的JSON键名和对应的值,折腾了好一会儿才搞定,分享一下整个过程和最终解决方案。

场景背景

我有一张jsondata表,其中jsonstr列存的是JSON格式的数据,示例内容如下:

{
  "key1": "value1",
  "key2": "value2",
  "key3": "value3"
}

对应的实体类JsondataEntity用Hashtable<String, Object>映射这个JSON列,搭配自定义的HashMapConverter实现JSON字符串与Hashtable的双向转换——这么做就是为了灵活存储任意JSON结构,同时支持动态查询不同的键值对。

初始尝试:硬编码键名的查询(正常工作)

一开始先写了个硬编码键名的测试查询,这个是能正常跑通的:

@Query(value = "SELECT j.id, j.jsonstr, JSON_UNQUOTE(j.jsonstr-> '$.key1') AS key1 FROM jsondata j " +
        "WHERE j.jsonstr-> '$.key1' = :value", nativeQuery = true)
List<Map<String, Object>> findAllByKeyValue(@Param("value") String value);

问题出现:动态传键名的查询(报错)

但当我尝试把键名做成参数动态传入时,直接拼接参数的写法触发了报错:

@Query(value = "SELECT j.id, j.jsonstr, JSON_UNQUOTE(j.jsonstr-> '$." + ":keyName"+ "') AS :keyName FROM jsondata j WHERE j.jsonstr -> '$." + ":keyName" + "' = :value", nativeQuery = true)
List<Map<String, Object>> findAllByKeyValues(@Param("keyName") String keyName, @Param("value") String value);

报错信息如下:

2023-10-31T16:14:23.277+01:00 ERROR 15448 --- [nio-8090-exec-1] o.a.c.c.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: org.springframework.dao.InvalidDataAccessResourceUsageException: JDBC exception executing SQL [SELECT j.id, j.jsonstr, JSON_UNQUOTE(j.jsonstr-> '$.:keyName') AS ? FROM jsondata j WHERE j.jsonstr -> '$.:keyName' = ?] [Invalid JSON path expression. The error is around character position 10.] [n/a]; SQL [n/a]] with root cause

问题根源很清晰:JDBC的参数绑定机制会把:keyName当成占位符处理,最终生成的SQL里JSON路径变成了$.:keyName,这完全不符合MySQL的JSON路径语法,自然触发了语法错误。

最终解决方案

后来在Bill Karwin的提示下,我改用CONCAT动态拼接合法的JSON路径,再配合JSON_EXTRACT函数实现动态查询,修改后的语句就能正常工作了:

@Query(value = "SELECT j.id, j.jsonstr, JSON_UNQUOTE(JSON_EXTRACT(j.jsonstr, CONCAT('$.', :keyName))) AS :keyName FROM jsondata j WHERE JSON_EXTRACT(j.jsonstr, CONCAT('$.', :keyName)) = :value", nativeQuery = true)
List<Map<String, Object>> findAllByKeyValues(@Param("keyName") String keyName, @Param("value") String value);

核心思路是:用CONCAT('$.', :keyName)动态生成符合语法的JSON路径字符串,再通过JSON_EXTRACT函数提取对应的值,既避开了参数绑定破坏JSON路径的问题,又完美实现了动态指定键名的需求。

备注:内容来源于stack exchange,提问作者davidvera

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.21 09:34:30