有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

带有空日期参数的java Hibernate本机查询

我在一个看似简单的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() {}
}

这是我的本机查询,调用时“date”可能为空:

@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参数已被截断为日期(即没有小时、分钟等),但数据库条目不是,这就是为什么我在比较的左侧部分使用date()sql函数

如果以空日期运行,则查询会因以下错误而崩溃:

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标准没有短路条件求值,所以第二部分总是求值。另一方面,Hibernate无法推断“date”类型,因为它为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);

编辑: 我也试过这个:

@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时不会计算第二个条件部分,我尝试了这种方法,使用了CASE WHEN。。然后语法:

@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) 个答案

  1. # 1 楼答案

    您根本不需要检查参数是否为null,因为anything = null返回false。你只需要比较:e.date = :date or e.date = DATE('2000-01-01')。 无论何时传入null,第一个值都将为false,并默认为常量比较

  2. # 2 楼答案

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

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

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

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

  3. # 3 楼答案

    您在where子句的第一个条件中使用了date参数,而不是实际的表格字段: 这应该是有效的:

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