JPA Criteria Builder equal方法失效:JSON字段布尔值匹配问题
嘿,这个问题我之前踩过类似的坑!核心原因是JSON_EXTRACT返回的布尔值是带引号的字符串形式,而不是数据库原生的布尔类型,导致JPA没法正确映射和比较,所以查不到数据。
你可以先在数据库控制台执行SELECT JSON_EXTRACT(result, '$.matched') FROM your_table;验证一下——返回的结果肯定是"false"(带双引号的字符串),而不是原生的布尔值false。你的代码里直接把JSON_EXTRACT的返回类型指定为Boolean.class,JPA尝试把字符串转成布尔值时会出现类型不匹配,自然就查不到数据了。而streaming_threshold是数字类型,JSON_EXTRACT返回的是没有引号的0.5,JPA能直接映射成Double,所以范围查询能正常工作。
这里有几种可行的解决办法,你可以根据自己使用的数据库类型选择:
1. 用JSON_UNQUOTE去除引号(推荐,MySQL适用)
MySQL提供了JSON_UNQUOTE函数,可以去掉JSON_EXTRACT结果的引号,把字符串形式的布尔值转成可被JPA识别的类型:
criteriaBuilder.equal( criteriaBuilder.function("JSON_UNQUOTE", Boolean.class, criteriaBuilder.function("JSON_EXTRACT", String.class, root.get("result"), criteriaBuilder.literal("$.matched")) ), false );
如果你的MySQL版本是8.0+,还可以用更简洁的JSON_VALUE函数(相当于JSON_UNQUOTE(JSON_EXTRACT(...))的简写):
criteriaBuilder.equal( criteriaBuilder.function("JSON_VALUE", Boolean.class, root.get("result"), criteriaBuilder.literal("$.matched")), false );
2. 直接比较带引号的字符串
既然JSON_EXTRACT返回的是带引号的字符串,你也可以直接用字符串形式的布尔值来比较:
criteriaBuilder.equal( criteriaBuilder.function("JSON_EXTRACT", String.class, root.get("result"), criteriaBuilder.literal("$.matched")), "\"false\"" // 注意要包含双引号 );
不过这种方式可读性稍差,而且不同数据库的JSON处理可能有差异,通用性不如第一种方法。
3. 用CAST转换为布尔类型(PostgreSQL等数据库适用)
有些数据库支持直接把JSON提取的结果CAST为布尔类型,比如PostgreSQL:
criteriaBuilder.equal( criteriaBuilder.function("CAST", Boolean.class, criteriaBuilder.function("JSON_EXTRACT", String.class, root.get("result"), criteriaBuilder.literal("$.matched")), criteriaBuilder.literal("BOOLEAN") ), false );
注意:这个方式依赖数据库的CAST语法,不同数据库的写法可能不同,需要根据实际情况调整。
内容的提问来源于stack exchange,提问作者codebot

