如何使用CriteriaBuilder实现PostgreSQL JSONB的?运算符查询
我来帮你搞定这个问题!你之前的代码报错,核心原因是用错了PostgreSQL的JSONB函数,还没正确处理字段的类型。
先理清楚你的需求:你要实现的SQL是select * from synonyms_table where synonym::jsonb ? 'good';,这里的?运算符在PostgreSQL中,对于JSONB数组来说,是用来检查数组里是否包含指定的字符串元素(不过要注意,这个运算符原本更多用于检查JSONB对象是否存在某个键,但对数组也能生效)。
你之前写的代码用了jsonb_extract_path_text,这个函数是用来提取JSONB对象里指定路径的文本值,完全不是你需要的“检查元素是否存在”的功能;而且你把synonym字段当成String类型处理,导致PostgreSQL找不到匹配参数类型的函数,这才抛出了“function jsonb_extract_path_text(character varying, character varying) does not exist”的错误——因为这个函数的第一个参数需要是jsonb类型,而不是varchar。
下面给你两种可行的正确实现方式:
方式一:直接调用?运算符对应的底层函数
PostgreSQL的?运算符,对应jsonb ? text的场景,底层调用的是jsonb_exists(jsonb, text)函数,我们可以在CriteriaBuilder里直接调用这个函数来实现需求:
Specification<SynonymsTable> specification = (root, query, criteriaBuilder) -> { // 将字段转换为Jsonb类型(不要用String类型) Expression<Jsonb> synonymJsonb = root.get("synonym").as(Jsonb.class); // 调用jsonb_exists函数,传入jsonb字段和要匹配的字符串 Expression<Boolean> exists = criteriaBuilder.function( "jsonb_exists", Boolean.class, synonymJsonb, criteriaBuilder.literal("good") ); return criteriaBuilder.isTrue(exists); };
方式二:用@>运算符(更适合数组包含场景)
如果你的synonym字段确实是JSONB数组,其实PostgreSQL更推荐用@>运算符来做数组包含查询,对应的SQL是synonym @> '["good"]'::jsonb,语义更清晰,也更贴合数组查询的场景。用CriteriaBuilder实现的代码如下:
Specification<SynonymsTable> specification = (root, query, criteriaBuilder) -> { Expression<Jsonb> synonymJsonb = root.get("synonym").as(Jsonb.class); // 把要匹配的字符串包装成JSONB数组 Jsonb targetArray = JsonbBuilder.create().fromJson("[\"good\"]", Jsonb.class); // 调用jsonb_contains函数(对应@>运算符) Expression<Boolean> contains = criteriaBuilder.function( "jsonb_contains", Boolean.class, synonymJsonb, criteriaBuilder.literal(targetArray) ); return criteriaBuilder.isTrue(contains); };
最后提醒一下:确保你的项目配置了正确的PostgreSQL方言(比如org.hibernate.dialect.PostgreSQL10Dialect或更高版本),这样Hibernate才能正确识别JSONB类型和对应的数据库函数。
备注:内容来源于stack exchange,提问作者atul.singh

