Spring Data Rest: “Date is null” query throws an postgres exception

笑着哭i 提交于 2021-01-21 06:37:08

问题


I use Spring Boot and Data Rest to create a simple microservice in Java8 and get a postgres exception.

My entity:

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

My repository interface:

@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);
    ...
}

If I call ".../findByXYZ?invoiceDate=2016-02-22", I'll get the following error message:

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

But it works, when I remove the ":invoiceDate IS NULL" part. How can I check, if the invoiceDate parameter is null?


回答1:


I believe @Bonifacio is correct in that Postgres is not able to determine the type of your @Param("invoiceDate") parameter when performing the IS NULL test. I have queries similar to yours that behave as expected with an in memory H2 database, but fail with the Postgres integration tests.

I was able to get around this by casting the parameter to a date like so:

@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)" 
    )



回答2:


I've spend some time looking at this because I really want to compare "date is null" in querys and that's the result:

When you use the "cast(foo.date as date) is null", it works OK if the field is not null. If the field is null this exception is throwed:

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

The solution is use coalesce:

coalesce(:date, null) is null

It works fine having or not data in the field tested.

My query example:

@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)"
        + "                 )"
        + "             )"
        + "       ) "

Hope it works for you!




回答3:


I have had similar problems with the field of type LocalDateTime and field Timestamp in postgres 9.5. I solved it converting it like this:

    @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);



回答4:


When you use JPA with Postgres there is a curious behavior which will cause error when you want to check if a parameter is null before checking the desired condition later.

The error occurs because when reading the query, JPA can't identify what is the parameter type, and then raising an error while interpreting the query.

The workaround to it is switch the positions of your parameter conditions, to first check if the condition desired is true, and then check if the parameter is null or not.

This way, JPA will be able to identify the correct parameter type , and the error should not be raised again.

Try the following query and see if it will work:

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


来源:https://stackoverflow.com/questions/35571624/spring-data-rest-date-is-null-query-throws-an-postgres-exception

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!