首页 > 解决方案 > JPA UTC 时区查询谓词问题

问题描述

我在将 Spring Boot Starter Parent 从 2.0.9.RELEASE 升级到 2.3.3.RELEASE 时遇到问题。更具体地说,我将问题追溯到从 2.0.9.RELEASE 迁移到 2.1.0.RELEASE。在 2.1.0.RELEASE 之前,我们所有的标准构建器谓词都像下面的例子一样进行相等比较。随着升级到 2.1.0.RELEASE,我们不再能够进行 equals 比较,因为没有返回匹配的行。

Predicate foreverDatePredicate = criteriaBuilder.equal(root.get("deactivatedAt"), LocalDateTime.of(9999, Month.DECEMBER, 31, 0, 0, 0))

看来这两个 Spring Boot 版本从 Hibernate Core 5.2.18.FINAL 到 5.3.7.FINAL,可能还有 JPA 2.1 到 2.2。我不确定这些版本更改是否与以下问题有关。

由于本地时区 (PST) 与存储在数据库中的时区 (UTC) 之间的时区不匹配,问题似乎正在发生。在我的 sql 编辑器中,我能够验证休眠查询是否正常工作,并在 where 子句中未提供时区时返回适当的行。我们所有的日期时间列都以 UTC 时间存储在我们的 MySQL 数据库中。我尝试在 application.properties 和我们的 Hibernate JPA 属性中使用以下时区相关属性,但两者似乎都不起作用或被完全忽略:

# application.properties
spring.jpa.properties.hibernate.jdbc.time_zone=UTC

# Used in jpaProperties property under entityManagerFactory
hibernate.jdbc.time_zone=UTC

正如其他一些线程所建议的那样,我可以将 JVM 时区设置为 UTC,并且在尝试后它可以工作;但是,我宁愿避免这样做,因为对于应该能够在更细粒度级别解决的问题来说,这似乎是一个影响深远的修复:

@PostConstruct
public void started() {
    TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
}

我最终将负责映射到 MySQL 日期时间列的 Hibernate 实体类型从 LocalDateTime 转换为 OffsetDateTime,如下所示。默认情况下,OffsetDateTime 以 UTC 时间存储带有时区的完整时间戳,这已经解决了问题,我们的 datetime equals 谓词再次起作用,但这引起了重大的重构问题。

从改变

@MappedSuperclass
public class BitemporalEntity implements Serializable {

    private static final long serialVersionUID = 2966895098986638738L;

    @Column(name = "deactivated_at")
    protected LocalDateTime deactivatedAt;

@MappedSuperclass
public class BitemporalEntity implements Serializable {

    private static final long serialVersionUID = 2966895098986638738L;

    @Column(name = "deactivated_at")
    protected OffsetDateTime deactivatedAt;

我只是想知道我是否在这里遗漏了什么。使用 OffsetDateTime 而不是 LocalDateTime 是解决问题的好方法还是有其他方法?

标签: javamysqlspringhibernatejpa

解决方案


推荐阅读