Spring Data Rest:“日期为空”查询引发 postgres 异常

Posted

技术标签:

【中文标题】Spring Data Rest:“日期为空”查询引发 postgres 异常【英文标题】:Spring Data Rest: "Date is null" query throws an postgres exception 【发布时间】:2016-06-04 22:34:50 【问题描述】:

我使用 Spring Boot 和 Data Rest 在 Java8 中创建了一个简单的微服务并获得了 postgres 异常。

我的实体:

@Entity
public class ArchivedInvoice implements Serializable 
    ...
    @Column
    private String invoiceNumber;
    @Column
    private java.sql.Date invoiceDate;
    ...

我的仓库界面:

@RepositoryRestResource(collectionResourceRel = "archivedinvoices", path = "archivedinvoices")
public interface ArchivedInvoiceRepository extends PagingAndSortingRepository < ArchivedInvoice, Long > 
    ...
@RestResource(rel = "findByXYZ", path = "findByXYZ")
@Query(value = "SELECT ai FROM ##entityName ai WHERE "
        + "(:invoiceNumber IS NULL OR ai.invoiceNumber LIKE :invoiceNumber) AND "
        + "(:invoiceDate IS NULL OR ai.invoiceDate = :invoiceDate)" 
        )
public Page < ArchivedInvoice > findByXYZ(
        @Param("invoiceNumber") @Nullable String invoiceNumber,
        @Param("invoiceDate") @Nullable Date invoiceDate,
        Pageable p);
    ...

如果我调用“.../findByXYZ?invoiceDate=2016-02-22”,我会收到以下错误消息:

org.springframework.dao.InvalidDataAccessResourceUsageException: could not extract ResultSet; SQL [n/a]; nested exception is org.hibernate.exception.SQLGrammarException: could not extract ResultSet
...
Caused by: org.hibernate.exception.SQLGrammarException: could not extract ResultSet
...
Caused by: org.postgresql.util.PSQLException: FEHLER: could not determine data type of parameter

但是,当我删除 ":invoiceDate IS NULL" 部分时,它可以工作。如果 invoiceDate 参数为空,如何检查?

【问题讨论】:

你可以试试这个***.com/a/27193644/2055854 【参考方案1】:

我花了一些时间来研究这个,因为我真的想在查询中比较“日期为空”,这就是结果:

当您使用“cast(foo.date as date) is null”时,如果该字段不为空,则它可以正常工作。如果该字段为 null,则抛出此异常:

org.postgresql.util.PSQLException: ERROR: cannot cast type bytea to date

解决方案是使用合并:

coalesce(:date, null) is null

无论是否有现场测试的数据,它都可以正常工作。

我的查询示例:

@Query("SELECT c "
        + " FROM Entity c "
        + " WHERE "
        + "       and ( (coalesce(:dateFrom, null) is null and coalesce(:dateUntil, null) is null) "
        + "             or ((coalesce(c.date, null) is not null) "
        + "                 and ( "
        + "                        ((coalesce(:dateFrom, null) is null and coalesce(:dateUntil, null) is not null) and c.date <= :dateUntil) "
        + "                     or ((coalesce(:dateUntil, null) is null and coalesce(:dateFrom, null) is not null) and c.date >= :dateFrom)"
        + "                     or (c.date between :dateFrom and :dateUntil)"
        + "                 )"
        + "             )"
        + "       ) "

希望它对你有用!

【讨论】:

我喜欢 coalesce 解决方案;它比铸造要短一些。 @Tales Kerschner 我假设您在 java 代码中使用 java.sql.Dates 作为 dateFrom 和 dateUntil ? java.util.Date 不能做同样的事情吧?【参考方案2】:

我在 postgres 9.5 中对 LocalDateTime 类型的字段和 Timestamp 字段有类似的问题。 我解决了它,像这样转换它:

    @Query("SELECT c "
        + "FROM Contract c "
        + "WHERE "
        + "(:idContract IS NULL OR c.idContract = :idContract) AND "
        + "(CAST(:dtCancelInitial AS java.time.LocalDateTime) IS NULL OR (c.dtCancel >= :dtCancelInitial AND c.dtCancel < :dtCancelFinal)) "
        + "ORDER BY c.idContract")
List<Contract> findContratConsultaCancelamento(@Param("idContract") Long idContract, @Param("dtCancelInitial") LocalDateTime dtCancelInitial, @Param("dtCancelFinal") LocalDateTime dtCancelFinal);

【讨论】:

这似乎没有回答这个问题。该问题没有提到Contract的表名。 它可能没有回答这个问题,但它向我展示了你也可以转换为 Java 类型,而我之前无法转换 uuid(即使使用评论中提到的“二进制”),这对我有很大帮助在另一个答案上)【参考方案3】:

我相信@Bonifacio 是正确的,因为在执行IS NULL 测试时,Postgres 无法确定您的@Param("invoiceDate") 参数的类型。我有与您的查询类似的查询,它们在内存 H2 数据库中的行为与预期一样,但在 Postgres 集成测试中失败。

我可以通过将参数转换为这样的日期来解决这个问题:

@Query(value = "SELECT ai FROM ##entityName ai WHERE "
    + "(:invoiceNumber IS NULL OR ai.invoiceNumber LIKE :invoiceNumber) AND "
    + "(cast(:invoiceDate as date) IS NULL OR ai.invoiceDate = :invoiceDate)" 
    )

【讨论】:

对于其他感兴趣的人,UUID 的转换类型是“binary”而不是“other”或“varchar” 这里是这个解决方案的解释:blog.mimacom.com/java-8-dates-with-postgresql【参考方案4】:

当您将 JPA 与 Postgres 一起使用时,当您想在稍后检查所需条件之前检查参数是否为空时,会出现一种奇怪的行为,这将导致错误。

发生错误是因为在读取查询时,JPA 无法识别参数类型,然后在解释查询时引发错误。

解决方法是切换参数条件的位置,首先检查所需条件是否为真,然后检查参数是否为空。

这样,JPA 将能够识别正确的参数类型,并且不会再次引发错误。

尝试以下查询,看看它是否有效:

@Query(value = "SELECT ai FROM ##entityName ai WHERE "
    + "(:invoiceNumber IS NULL OR ai.invoiceNumber LIKE :invoiceNumber) AND "
    + "(ai.invoiceDate = :invoiceDate OR :invoiceDate IS NULL)" 
    )

【讨论】:

不幸的是,这不起作用。但这是个好主意!谢谢! 这对我来说适用于查询中的第一个日期参数,而不适用于发送日期参数。我在查询中有两个日期参数。它抱怨第二个也是一样的。 这行得通,因为它是在我的数据库中以这种方式实现的。我相信你的 postgres 日志中可能有一个烦人的“类型转换”错误。但话又说回来,我想可能是因为我使用的是 java.sql.Date 数据类型

以上是关于Spring Data Rest:“日期为空”查询引发 postgres 异常的主要内容,如果未能解决你的问题,请参考以下文章

自定义查询 Spring Data JPA + REST

Spring Data JPA - 使用 REST 调用上的查询参数从方法名称生成查询

Spring Data REST 的 QueryDSL 集成,用于查询实体中集合映射的子属性

如何使用 Querydsl 和 Spring Data 轻松实现“REST API 查询语言”来过滤实体?

Spring Data REST 的 QueryDSL 集成可以用来执行更复杂的查询吗?

Spring Data Rest with Spring Security - 按当前用户查找所有内容