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

如何用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 18:20:42