web-dev-qa-db-ja.com

日付パラメーターがnullのHibernateネイティブクエリ

私は一見単純な休止状態のユースケースに苦労しており、何が起こっているのか理解できません:

これは私の実体です:

@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() {}
}

そして、これは潜在的に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);

関数に渡された日付パラメーターは既に日付(つまり、時間、分など)として切り捨てられていませんが、データベースエントリは切り捨てられていないため、比較の左側でDATE()SQL関数を使用しています。

NULLの日付で実行すると、クエリは次のエラーでクラッシュします。

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.

私の理解から、SQL標準では条件評価が省略されないため、2番目の部分は常に評価されます。一方、Hibernateは「日付」タイプがnullであるため推測できないため、リクエストにバイナリとして挿入されるため、エラーが発生します。

私はこの亜種を試しましたが、同じ結果が得られました:

@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:私もこれを試しました:

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

同じ問題を反映する別のエラーで:

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

EDIT2:

パラメータがNULLの場合に2番目の条件部分が評価されないようにするために、CASE WHEN .. THEN構文を使用してこのアプローチを試しました。

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

しかし、Hibernateはそれを気に入らず、なぜかわかりません...

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)]
3
Neumann

Dateパラメーターに@Temporalを追加すると、Spring Dataは、そのパラメーターがnullであっても、Hibernateにそのパラメーターを提示する方法を認識します。

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

(パラメーターはJava.util.DateではなくJava.sql.Dateのタイプである必要があります)。

「SQL/Hibernateがショートできないのはなぜですか」への別の注記:パフォーマンス上の理由から述語を並べ替えることは、ほとんどのデータベースの主要な機能であり、そのためには最初にパラメーターをバインドする必要があります。ほとんどのデータベースは実際に述語を短絡しますが、クエリに書き込んだ順序とは限りません。

1
Tobias Liefke

_anything = null_はfalseを返すため、パラメーターがnullかどうかを確認する必要はまったくありません。比較が必要なのは、e.date = :date or e.date = DATE('2000-01-01')だけです。最初のものは、nullを渡すと常にfalseになり、デフォルトでは定数比較になります

0
eduyayo