Postgres & JPA 命名查询 - WHERE 中的任何 (*) 值

Posted

技术标签:

【中文标题】Postgres & JPA 命名查询 - WHERE 中的任何 (*) 值【英文标题】:Postgres & JPA named queries - any (*) value in WHERE 【发布时间】:2019-06-09 09:24:36 【问题描述】:

我想为一些特定的 GET 案例编写过滤查询。

我现在正在做的是为每个案例编写单独的查询,这非常耗时。相反,我想编写一个大的命名查询,如果设置了过滤器,我可以在 WHERE 子句中输入一个值,否则为 ANY。 JPA可以做到这一点吗? 这是一个示例控制器,它通过特定的客户端 ID 返回付款:

public List<PaymentEntity> findAllByClientId(final int page, final int pageSize, final String fromDate,
        final String toDate, final Long clientId) 
    Map<String, Object> parameters = new HashMap<>();
    parameters.put("clientId", clientId);
    return super.findWithNamedQueryPagination("PaymentEntity.findAllWithPaginationByClientId", parameters,
            PaymentEntity.class, page, pageSize, fromDate, toDate);

这是属于命名查询:

SELECT i FROM PaymentEntity i WHERE i.clientContractData.client.id = :clientId AND i.createdAt BETWEEN :fromDate AND :toDate ORDER BY i.createdAt DESC

我现在想要的是能够像这样传递一个参数:

    if(clientId == null) 
        parameters.put("clientId", "*");
     else 
        parameters.put("clientId", clientId);
    

但现在我当然会收到类型错误:Parameter value [*] did not match expected type [java.lang.Long (n/a)]

实现这一目标的最佳方法是什么?我必须编写本机查询还是有命名查询的方法?

编辑澄清:上面的示例显示了一个类型为 Long 的查询,但我也需要使用 String 执行此操作。

【问题讨论】:

【参考方案1】:

您可以尝试更改您的查询,例如:

WHERE (i.clientContractData.client.id = :clientId or :clientId = -1)

注意:我认为 JPA 中的引用可以达到 2 个级别,而这里有 3 个级别:

i.clientContractData.client.id

您可能需要在此处使用额外的显式连接

【讨论】:

你是说:clientId is null 吗? 我想由 OP 决定使用什么作为任何值 我编辑了这个问题。这是 Long 类型的一个很好的解决方案,但不幸的是我需要一个也适用于字符串的解决方案。对于级别 - 它使用 3 个级别 :) 我为这种情况编写了一些测试。 似乎 3-Level-Deep 引用确实是 WHERE 子句结合 OR 语句的问题。当我将客户端存储在付款中并这样做时,它可以工作:SELECT i FROM PaymentEntity i WHERE (:clientId IS NULL OR i.client.id = :clientId) AND i.createdAt BETWEEN :fromDate AND :toDate ORDER BY i.createdAt DESC【参考方案2】:

更新查询,

SELECT i FROM PaymentEntity i WHERE 
       ((i.clientContractData.client.id = :clientId and :clientId IS NOT NULL) 
        or (i.clientContractData.client.id IS NOT NULL AND :clientId IS NULL)) 
         AND i.createdAt BETWEEN :fromDate AND :toDate ORDER BY i.createdAt DESC

如果 clientId 为 null,则发送 null 值:

if(clientId == null) 
    parameters.put("clientId", null);
 else 
    parameters.put("clientId", clientId);

对于更复杂的情况,您使用 rsql-jpa-specification 项目:

https://github.com/perplexhub/rsql-jpa-specification

【讨论】:

这看起来是一个很好的长字符串解决方案,我今天会试试这个并回复你!虽然当我有很多值要过滤时查询会变得很长:) 不幸的是,这只会给我PaymentEntity 也有相关clientContractData 的结果,但默认情况下我需要所有条目。其实我不明白为什么这不起作用:SELECT i FROM PaymentEntity i WHERE (:clientId IS NULL OR i.clientContractData.client.id = :clientId) AND i.createdAt BETWEEN :fromDate AND :toDate ORDER BY i.createdAt DESC。这也只返回带有 clientcontractdata 的付款。也许这是由于@Maciej Kowalski 提到的 3-Level-Deep 嵌套?

以上是关于Postgres & JPA 命名查询 - WHERE 中的任何 (*) 值的主要内容,如果未能解决你的问题,请参考以下文章

如何在 jpa 查询中调用 postgres position() 函数

Postgres Interval 不适用于本机 Spring 数据 JPA 查询

Spring Data JPA 方法或查询以使用 Postgres 列执行算术运算

如何根据postgres的jsonb列的where条件(无本机查询)使用jpa在spring boot中选择数据?

JPA Native 查询为 INSERT INTO VALUES 中的 List 参数放置括号错误

JPA XML 查询文件