Hibernate检索UTC时间戳

3pvhb19x  于 2023-01-13  发布在  其他
关注(0)|答案(3)|浏览(156)

我正在使用hib + spring,希望以UTC格式存储/加载时间戳。我读到应该添加一个属性,所以我将其添加到我的application.properties

spring.jpa.properties[hibernate.jdbc.time_zone]=UTC

这解决了问题的一部分-现在数据库中的日期是以UTC保存的。但是当我检索时间戳时,它们被转换为默认时区。如果不将默认时区设置为UTC,我如何修复这个问题?实体的属性类型为LocalDateTime。我运行了代码,并注意到在get期间使用了正确的结果集方法(接受日历的那个)与具有存储UTC的区域信息的示例。但是在将日历的值设置为从数据库检索的值之后,日历将转换为带有以下代码的时间戳

Timestamp ts = new Timestamp(c.getTimeInMillis());

在调试模式下,我看到ts存储了cdate字段,其中的时间戳值是默认时区(而不是UTC)。

gijlo24d

gijlo24d1#

首先,如果我们讨论Hibernate 5(准确地说是5.2.3 - 5.6.x),hibernate.jdbc.time_zone设置的目的不是给予应用程序开发人员能够实现某种复杂的日期/时间逻辑,而是让持久性提供程序与底层数据库同步,这在corresponding CR中有明确的说明:
目前我的数据库有UTC的隐式日期时间。没有分区数据附加到字符串的末尾(例如“2013-10-14 04:00:00”)。当Hibernate将其作为ZonedDateTime读取时,它错误地将其作为EST读取,因为这是JVM的时区。如果能够通过注解指定字段的时区,那就太好了。
基本上:如果(我的:并且仅当)SQL语句,如SELECT SYSDATE FROM DUALSELECT LOCALTIMESTAMP for PostgreSQL,etc)返回一些您不期望的东西,在这种情况下Hibernate将开始调整非时区感知JDBC数据,使其对于应用程序来说或多或少可靠-这正是您所观察到的(* 当我检索时间戳时,它们被转换为默认时区 *)
其次,任何关于JSR-310和JDBC 4.2的推测(比如对于时区感知的java类型,您需要将DB列定义为timestamp with time zone)在Hibernate 5的情况下都是不正确的,这在corresponding CR中也提到了:
“stored TZ”的整个想法实际上取决于数据库/驱动程序如何对待TIMESTAMP以及它是否支持一个“TIMESTAMP_WITH_TIMEZONE”类型,我个人认为将具体的TZ差异保存到DB中是一个巨大的错误,所以我个人仍然不支持TIMESTAMP_WITH_TIMEZONE类型,这意味着我们永远不必绑定Calendar,因为我们可以简单地将值转换为JVM/具体地说,我建议我们(继续)假设驱动程序已经设置好了,这样当...
事实上,如果您试图在Hibernate 5源代码中查找java.sql.Types#TIMESTAMP_WITH_TIMEZONE的用法,您将一无所获,这仅仅是因为当时Hibernate开发人员对于在不同Java版本、Java类型、DB引擎和JDBC驱动程序的情况下时区转换应该如何工作没有达成共识(他们正在开发最流行的(mine:唯一一个)JPA实现,这肯定与开发微服务不同),但是,Hibernate 6中有很多相关的变化(例如,检查TimeZoneStorageType)。在Hibernate 5中,所有时区转换逻辑都通过TimestampTypeDescriptor:

@Override
protected X doExtract(ResultSet rs, String name, WrapperOptions options) throws SQLException {
  return options.getJdbcTimeZone() != null ?
   javaTypeDescriptor.wrap( rs.getTimestamp( name, Calendar.getInstance( options.getJdbcTimeZone() ) ), options ) :
   javaTypeDescriptor.wrap( rs.getTimestamp( name ), options );
}

如您所见,Hibernate 5只是向JDBC驱动程序提供了一个提示,即后者应如何处理#getTimestamp调用:
使用给定的Calendar对象构造Timestamp对象,将JDBC TIMESTAMP参数的值作为java.sql.Timestamp对象进行检索。使用Calendar对象,驱动程序可以在考虑自定义时区和区域设置的情况下计算时间戳。如果未指定Calendar对象,驱动程序将使用默认时区和区域设置。
关于你情况:
您要么需要使用支持时区的Java类型(ZonedDateTime/OffsetDateTime,甚至Instant),要么编写自己的Hibernate类型,它将处理时区转换--这并不像看起来那么难。

6kkfgxo0

6kkfgxo02#

我们也可以设置它'每会话的基础:

session = HibernateUtil.getSessionFactory().withOptions()
  .jdbcTimeZone(TimeZone.getTimeZone("UTC"))
  .openSession();
7ajki6be

7ajki6be3#

我的数据库时区是UTC,而在我的应用程序时区中,我通过让实体和表都有一个UTC日期来解决这个问题,这样它们之间就不需要转换了。然后我在getter和setter中用代码完成了时间戳之间的转换。然后我想你可以手动完成。

该字段的Setter和getter

public void setCreatedDate(LocalDateTime createdAt)
{         
    this.createdAt = createdAt.atZone(ZoneId.systemDefault()).withZoneSameInstant(ZoneOffset.UTC).toLocalDateTime();
}

public LocalDateTime getCreatedDate()
{         
    return createdAt.atZone(ZoneId.systemDefault()).withZoneSameInstant(ZoneOffset.UTC).toLocalDateTime();
}

相关问题