简体   繁体   中英

Hibernate native query with null Date parameter

I'm struggling on a seemingly simple hibernate use-case and cannot figure out what's going on :

This is my entity :

@Entity
@Data
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class Event {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;

    @Column(columnDefinition = "timestamp without time zone", nullable = false)
    @Temporal(TemporalType.TIMESTAMP)
    private Date date;

    @Column(columnDefinition = "text")
    private String status;

    public Event() {}
}

And this is my native query, called with 'date' being potentially NULL :

@Query(value = "SELECT e FROM Event e WHERE :date is null or DATE(e.date) = :date")
Page<Event> findEvents(Pageable pageable, @Param("date") Date date);

The date parameter passed to the function is already truncated as a date (ie no hours, minutes, etc), but the database entry isn't, that's why I use the DATE() sql function in the left part of the comparison.

Where run with a NULL date, the query crashes with this error :

Caused by: org.postgresql.util.PSQLException: ERROR: operator does not exist: date = bytea
  Indice : No operator matches the given name and argument type(s). You might need to add explicit type casts.

From what I understand, SQL standard doesn't short-circuit condition evaluations, so the second part is always evaluated. On the other hand, Hibernate is unable to infer the 'date' type because it's null, so it's injected in the request as binary, hence the error.

I tried this variant, with the same results :

@Query(value = "SELECT e FROM Event e WHERE :date is null or DATE(e.date) = COALESCE(:date, '2000-01-01')")
Page<Event> findEvents(Pageable pageable, @Param("date") Date date);

EDIT : I also tried this one :

@Query(value = "SELECT e FROM Event e WHERE :date is null or DATE(e.date) = CAST(:date AS date)")

With another error that reflects the same issue :

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

EDIT2 :

To make sure that the second condition part is never evaluated if the parameter is NULL, I tried this approach, using the CASE WHEN .. THEN syntax :

@Query(value = "SELECT e FROM Event e WHERE (case when :date is null then true else (DATE(e.date) = cast(:date AS date)) end)"

But Hibernate doesn't like it, and I don't know why ...

Caused by: org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected AST node: case near line 1, column 30 [SELECT e FROM Event e WHERE (case when :date is null then true else (DATE(e.date) = cast(:date AS date)) end)]

If you add @Temporal to your Date parameter, Spring Data knows how to present that parameter to Hibernate, even if it is null :

Page<Event> findEvents(Pageable pageable, @Param("date") @Temporal Date date);

(The parameter must be of type java.util.Date , not java.sql.Date ).

Another note to "Why is SQL / Hibernate not able to short-circuit": It is a key feature of most databases to reorder the predicates for performance reasons - and to do so the parameters must be bound first. Most databases will indeed short-circuit the predicates, but not necessarily in the order you write them into the query.

You won't need to check if the parameter is null at all because anything = null returns false. You only need the comparisons: e.date = :date or e.date = DATE('2000-01-01') . The first one will be false whenever you pass in a null and defaults to the constant comparison

You have used the date param instead of the actual table field in the first condition of your where clause: This should work:

@Query(value = "SELECT e FROM Event e WHERE e.date is null or DATE(e.date) = :date")
Page<Event> findEvents(Pageable pageable, @Param("date") Date date);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM