在 queryDSL 中使用 postgresql 数组覆盖运算符的语法

Posted

技术标签:

【中文标题】在 queryDSL 中使用 postgresql 数组覆盖运算符的语法【英文标题】:Syntax for using postgresql array overlay operator in queryDSL 【发布时间】:2021-01-16 15:23:39 【问题描述】:

我正在尝试构建一个查询,该查询在 queryDSL + JPA 中使用 postgresql 的数组覆盖运算符,使用 BooleanBuilder 和布尔模板,但是我无法让 JPA 接受我的查询。我目前写的方式是

    public BooleanBuilder addUUIDListIntersectionCondition(BooleanBuilder clause,
        CollectionPath<UUID, ComparablePath<UUID>> arg0, List<UUID> arg1) 
        clause.and(Expressions.booleanTemplate("0 && ARRAY[1::UUID]", arg0,
            String.join(",", arg1.stream().map(a -> a.toString()).collect(Collectors.toList()))));
    

但这会导致以下异常:

An exception occurred while creating a query in EntityManager: 
Exception Description: Syntax error parsing [select ec
from vw_ec ec
where ec.orgUserIds && ARRAY[?1::UUID]]. 
[76, 193] The expression is not a valid conditional expression.; nested exception is java.lang.IllegalArgumentException: An exception occurred while creating a query in EntityManager: 
Exception Description: Syntax error parsing [select ec
from vw_ec ec
where ec.orgUserIds && ARRAY[?1::UUID]]. 
[76, 193] The expression is not a valid conditional expression.
    at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:374)
    at org.springframework.orm.jpa.DefaultJpaDialect.translateExceptionIfPossible(DefaultJpaDialect.java:128)
    at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:528)
    at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:61)
    at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:242)

直接在 postgresql 中运行类似的查询按预期工作:

select *
    from ec ec
    where (ec.orgUserIds && array['c61d452e-0361-11eb-8ecd-9e48108033fa', 'c623fc2d-0361-11eb-8ecd-9e48108033fa'::UUID])

关于我哪里出错了有什么想法吗?

【问题讨论】:

我尝试将覆盖操作符包装在数据库函数中并调用它,但现在我得到内部异常:java.lang.ClassCastException: class org.eclipse.persistence.mappings.structures.ArrayMapping当我尝试调用该函数时,无法将其转换为类 org.eclipse.persistence.mappings.CollectionMapping。 如果您已经弄清楚应该如何使用完全相同的查询 ec.orgUserIds && array['c61d452e-0361-11eb-8ecd-9e48108033fa', 'c623fc2d-0361- 11eb-8ecd-9e48108033fa']::uuid[]。我遇到了同样的问题。 我已经设法使这样的查询可行 c.orgUserIds && array['c61d452e-0361-11eb-8ecd-9e48108033fa']::uuid[] 其中 arg1 只有一个使用 booleanTemplate 的元素: "Expressions.booleanTemplate("0 && ARRAY[1]::uuid[]", ec.orgUserIds, StringUtils.collectionToDelimitedString(arg1, ",", "'", "'"))" 但是这个语法如果 arg1 有超过 1 个元素,则对我不起作用 - 抛出 PostgresqlBadGrammarException:不正确的 UUID 长度。 QueryDSL 模板看起来像是将 ARRAY 的整个输入参数解释为字符串。使用 string_to_array 而不是 ARRAY 对我也没有帮助 【参考方案1】:

不要认为我有干净的解决方案,但它适用于以下查询:

select *
from ...
where 'e48f54d5-9845-4987-a53d-e0ecfe3dbb43,e48f54d5-9845-4987-a53d-e0ecfe3dbb45'::uuid[] && 'e48f54d5-9845-4987-a53d-e0ecfe3dbb40,e48f54d5-9845-4987-a53d-e0ecfe3dbb45'::uuid[];

我在booleanTemplate 中使用以下语法:

select(...)
.from(...)
.where(new BooleanBuilder().and(Expressions.booleanTemplate("0 && 1::uuid[]", arg0, String.format("%s", arg1.stream().map(UUID::toString).collect(joining(",")))))

【讨论】:

哦,很有趣,所以您可以通过在 select 语句中而不是在 where 中使用 booleanTemplate 来使其工作?这比不能全部使用要好,但如果能够按功能过滤也很好。 我一直在尝试您的建议,但在查询实际执行之前,我总是在 JPA 中遇到错误。 [1258, 1504] 表达式不是有效的条件表达式。 [1504, 1814] 查询包含格式错误的结尾。] 2020-10-16 22:09:57.230 调试 10 --- [nio-8080-exec-6] o.j.s.OpenEntityManagerInViewInterceptor:在 OpenEntityManagerInViewInterceptor 中关闭 JPA EntityManager 我更正了答案,它会清楚一点。 BooleanTemplate 用于 where 子句。试试这个例子。

以上是关于在 queryDSL 中使用 postgresql 数组覆盖运算符的语法的主要内容,如果未能解决你的问题,请参考以下文章

使 JPQL/QueryDSL 不会产生可怕的查询

“.”处或附近的QueryDSL语法错误在多对多的关系

如何使用 QueryDSL 在查询中使用 SAMPLE 关键字

QueryDSL:在 JPAQuery.from() 中使用 JPASubQuery

在 MongoDB 中使用 QueryDSL - java.lang.NoClassDefFoundError

使用 Redshift 设置的 QueryDSL