【问题标题】:Hibernate Ignores DB Timezone Info when Binding to Java Timestamp Object绑定到 Java 时间戳对象时,Hibernate 忽略 DB 时区信息
【发布时间】:2018-07-23 07:49:36
【问题描述】:

我在 Oracle 中存储了一些日期,Oracle 的 TIMESTAMP(3) 作为其数据类型。现在我正在编写一个 Spring Boot 应用程序来读取这些值。代码如下:

HibernateCallback callback = new HibernateCallback() {
        public Object doInHibernate(Session session) throws HibernateException {
            Query query = session.createSQLQuery("SELECT date_field FROM some_table WHERE some_conditions");

            return query.list();
        }
    };

所以:

List results = (List)getHibernateTemplate().execute(callback);
// suppose there's only one row and one column returned
Timestamp ts = result.get(0)[0];

返回由 Hibernate 自动创建的 Java Timestamp 对象。问题是,在构造对象时,它忽略了存储在 Oracle 中的时区,而是使用 JVM 的默认时区。我测试的是通过为JVM设置不同的时区,每次生成不同的时间戳。

这显然是错误的。日期在时间线上应该是唯一的。它不应该依赖于 JVM 的时区。我想知道在解析日期时包含数据库时区信息的正确方法是什么。现在看来它只是使用存储在 Oracle 中的字符串表示形式并使用 JMV 的时区对其进行解析。我正在使用休眠4.3.4.Final

PS:实际查询是高度定制的,所以我必须编写原始 SQL。

【问题讨论】:

  • 时间戳始终采用 UTC。如果你想要一个不同的时区,你必须使用ZonedDateTime自己转换它。
  • 是您的Oracle column data type TIMESTAMPTIMESTAMP WITH TIME ZONE 还是TIMESTAMP WITH LOCAL TIME ZONE?每个都非常不同。
  • @BasilBourque 这是 TIMESTAMP(3);
  • @JFreebird 澄清应该作为对问题的编辑而不是作为评论发布。

标签: java oracle hibernate date


【解决方案1】:

似乎问题在于日期处理的概念很少

  • 如果要在多个时区中使用,DB 中的日期应该是 UTC。
  • Java 程序应将日期转换为所需的时区。

如果您将数据库中的日期视为基于 UTC,然后检查休眠查询的输出,您应该会看到日期已根据 JVM 的时区更改。

如果您想要返回相同的日期(就像在 DB 中一样),也许您应该将日期转换为 UTC 时区。

现在,您可以执行以下任一操作:

  • 设置JVM的时区和数据库记录时区一样。不推荐
  • 根据 UTC 更改数据库中的日期,并将日期更改为 JVM 的时区。推荐

【讨论】:

  • DB 中的日期不一定是 UTC,它们可以在任何时区,具体取决于插入。
  • 是的@JFreebird。这就是为什么我说“应该”
【解决方案2】:

基本上,这甚至不是 Hibernate 的问题,而是 JDBC 的问题。默认情况下,JDBC 驱动程序将使用运行 JVM 的系统时区。如果您在不同的时区连接到数据库服务器,或者即使您想独立于系统的当前时区,最好明确设置 JDBC 时区。 您可以使用hibernate.jdbc.time_zone 属性设置时区或在运行时通过。

session = sessionFactory.withOptions()
.jdbcTimeZone(TimeZone.getTimeZone("UTC"))
.openSession();

对于 Oracle,我想说您可以使用 TIMESTAMP WITH LOCAL TIME ZONE,这将尊重您的 JDBC 客户端时区。

【讨论】:

  • 有没有办法让 JDBC 在检索 Date 列时尊重 DB 的时区?假设它在 PST 中,那么 JDBC 也应该使用 PST 将其绑定到 Java 对象。假设插入来自不同的流程。我只是不想硬编码 JDBC 时区。
  • @JFreebird JDBC 规范没有指定类似的东西,但也许某些 JDBC 驱动程序可以做到这一点。但似乎 Oracle JDBC 无法做到这一点,因为基本上并不总是要求连接到服务器的客户端应该有权访问服务器配置数据,如时区。不过,也许我错了。但通常,如果要从多个时区使用数据库服务器,它总是使用 UTC 时区。这是某种“最佳实践”。
【解决方案3】:

问题在于您使用的 Oracle 列数据类型,如果您访问官方 oracle 文档link,您会注意到 TIMESTAMP 不尊重时区,因此您应该使用TIMESTAMP WITH TIME ZONETIMESTAMP WITH LOCAL TIME ZONE

【讨论】:

  • 那么 TIMESTAMP 使用什么时区呢?目前在我们的 Oracle 数据库中,它使用 PST。我认为这也与 Hibernate 有关。 Hibernate 正在使用 JVM 的时区将选定的日期转换为 Java Date 对象。
猜你喜欢
  • 2016-10-13
  • 2015-02-07
  • 1970-01-01
  • 1970-01-01
  • 2014-09-21
  • 2014-01-09
  • 1970-01-01
  • 2017-07-13
  • 1970-01-01
相关资源
最近更新 更多