带有空日期参数的休眠本机查询

Neu*_*ann 3 java database hibernate jpa jakarta-ee

我在一个看似简单的休眠用例上苦苦挣扎,无法弄清楚发生了什么:

这是我的实体:

@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() {}
}
Run Code Online (Sandbox Code Playgroud)

这是我的本机查询,以 'date' 可能为 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);
Run Code Online (Sandbox Code Playgroud)

传递给函数的日期参数已经被截断为日期(即没有小时、分钟等),但数据库条目不是,这就是我在比较的左侧部分使用 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.
Run Code Online (Sandbox Code Playgroud)

据我了解,SQL 标准不会短路条件评估,因此始终评估第二部分。另一方面,Hibernate 无法推断“日期”类型,因为它是空的,所以它作为二进制注入到请求中,因此出现错误。

我尝试了这个变体,结果相同:

@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);
Run Code Online (Sandbox Code Playgroud)

编辑: 我也试过这个:

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

另一个错误反映了同样的问题:

Caused by: org.postgresql.util.PSQLException: ERROR: cannot cast type bytea to date
Run Code Online (Sandbox Code Playgroud)

编辑2:

为了确保在参数为 NULL 时永远不会评估第二个条件部分,我尝试了这种方法,使用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)"
Run Code Online (Sandbox Code Playgroud)

但是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)]
Run Code Online (Sandbox Code Playgroud)

Tob*_*fke 7

如果您添加@Temporal到您的Date参数,Spring Data 知道如何将该参数呈现给 Hibernate,即使它是null

Page<Event> findEvents(Pageable pageable, @Param("date") @Temporal Date date);
Run Code Online (Sandbox Code Playgroud)

(参数的类型必须是java.util.Date,而不是java.sql.Date)。

“为什么 SQL / Hibernate 不能短路”的另一个说明:大多数数据库的一个关键特性是出于性能原因重新排序谓词 - 为此必须首先绑定参数。大多数数据库确实会短路谓词,但不一定按照您将它们写入查询的顺序。