如何用CriteriaBuilder查询jsonb列中数组是否包含指定值?
解决方案:用CriteriaBuilder实现PostgreSQL jsonb数组包含判断
首先,你原来的SQL语句t.data->>'categorys' @> '17'其实是字符串包含判断(因为->>返回的是文本格式的数组字符串,比如"[\"17\",\"18\"]"),而非真正的jsonb数组包含。如果你的需求是判断jsonb数组中是否存在指定元素,正确的PostgreSQL写法应该是:
SELECT * FROM myScheme.myTable t where t.data->'categorys' @> '["17"]'::jsonb;
针对这个需求,用CriteriaBuilder可以通过以下几种方式实现:
方法1:调用PostgreSQL的jsonb_contains函数
PostgreSQL的jsonb_contains函数等价于@>运算符,我们可以直接通过CriteriaBuilder.function调用它:
判断数组包含任意一个指定值
if (Objects.nonNull(filter.getCategorias())) { Predicate[] categoryPredicates = filter.getCategorias().stream() .map(category -> { // 构建单个值的jsonb数组字面量,比如 ["17"] String jsonbArray = String.format("[\"%s\"]", category); return builder.isTrue( builder.function("jsonb_contains", Boolean.class, root.get("data").get("categorys"), // 直接获取jsonb类型的categorys字段 builder.literal(jsonbArray) ) ); }) .toArray(Predicate[]::new); predicates.add(builder.or(categoryPredicates)); }
判断数组包含所有指定值
if (Objects.nonNull(filter.getCategorias()) && !filter.getCategorias().isEmpty()) { // 构建包含所有目标值的jsonb数组 String jsonbArray = "[" + filter.getCategorias().stream() .map(c -> "\"" + c + "\"") .collect(Collectors.joining(",")) + "]"; predicates.add(builder.isTrue( builder.function("jsonb_contains", Boolean.class, root.get("data").get("categorys"), builder.literal(jsonbArray) ) )); }
方法2:直接模拟@>运算符
如果更倾向于直接使用@>运算符的逻辑,可以通过CriteriaBuilder.function构建表达式:
if (Objects.nonNull(filter.getCategorias())) { Predicate[] categoryPredicates = filter.getCategorias().stream() .map(category -> { Expression<Jsonb> categoryArray = builder.function("jsonb_extract_path", Jsonb.class, root.get("data"), builder.literal("categorys")); Expression<String> targetJson = builder.literal(String.format("[\"%s\"]", category)); return builder.isTrue( builder.function("operator", Boolean.class, builder.literal("@>"), categoryArray, targetJson ) ); }) .toArray(Predicate[]::new); predicates.add(builder.or(categoryPredicates)); }
原代码失效原因
你原来用jsonb_extract_path_text提取的是数组的文本字符串(比如"[\"17\",\"18\"]"),然后调用.in(filter.getCategorias()),这是在判断这个完整字符串是否等于集合中的某个值(比如判断"[\"17\",\"18\"]"是否等于"17"),逻辑完全不符合数组包含的需求,因此无法得到预期结果。
内容的提问来源于stack exchange,提问作者Eduardojls
相关产品推荐
相关产品推荐

