私は一見単純な休止状態のユースケースに苦労しており、何が起こっているのか理解できません:
これは私の実体です:
@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)]
Date
パラメーターに@Temporal
を追加すると、Spring Dataは、そのパラメーターがnull
であっても、Hibernateにそのパラメーターを提示する方法を認識します。
Page<Event> findEvents(Pageable pageable, @Param("date") @Temporal Date date);
(パラメーターはJava.util.Date
ではなくJava.sql.Date
のタイプである必要があります)。
「SQL/Hibernateがショートできないのはなぜですか」への別の注記:パフォーマンス上の理由から述語を並べ替えることは、ほとんどのデータベースの主要な機能であり、そのためには最初にパラメーターをバインドする必要があります。ほとんどのデータベースは実際に述語を短絡しますが、クエリに書き込んだ順序とは限りません。
_anything = null
_はfalseを返すため、パラメーターがnullかどうかを確認する必要はまったくありません。比較が必要なのは、e.date = :date or e.date = DATE('2000-01-01')
だけです。最初のものは、nullを渡すと常にfalseになり、デフォルトでは定数比較になります