当前位置: 首页 > 知识库问答 >
问题:

spring.jpa.properties.hibernate.jdbc.time_zone应用于写入而不是读取?

薛英卫
2023-03-14

我用的是:

  • Spring启动 2.0.4.发布
  • Spring数据-JPA 2.0.9.发布
  • Hibernate核心 5.2.17.最终版
  • hibernate-jpa-2.1-api 1.0.0.final
  • Postgres JDBC 驱动程序 42.2.9

我有以下实体:

@Entity
@EntityListeners(AuditingEntityListener.class)
public class MyEntity implements Serializable
{
    @Column(nullable = false, updatable = false)
    @CreatedDate
    private LocalDateTime createdDate;

    @Column(nullable = false)
    @LastModifiedDate
    private LocalDateTime lastModifiedDate;

    public LocalDateTime getCreatedDate()
    {
        return createdDate;
    }

    public LocalDateTime getLastModifiedDate()
    {
        return lastModifiedDate;
    }
}

以及在应用程序.yaml 中设置的以下属性:

spring:
  jpa:
    properties:
      hibernate:
        jdbc:
          time_zone: UTC

不管JVM时区/默认时区是什么,我都希望以UTC格式保存和返回时间戳。

出于测试目的,我将应用程序代码的时区设置为美国/夏威夷

TimeZone.setDefault(TimeZone.getTimeZone("US/Hawaii"));

当我保存一个实体时,它会用UTC时间戳正确地写入数据库:

[16:43:04.636Z #4c5.042 TRACE -            -   ] o.h.t.d.sql.BasicBinder: binding parameter [1] as [TIMESTAMP] - [2020-03-02T06:43:04.581]
[16:43:04.645Z #4c5.042 TRACE -            -   ] o.h.t.d.sql.BasicBinder: binding parameter [2] as [TIMESTAMP] - [2020-03-02T06:43:04.581]
[16:43:04.649Z #4c5.042 TRACE -            -   ] o.h.r.j.i.ResourceRegistryStandardImpl: Closing prepared statement [HikariProxyPreparedStatement@336047848 wrapping insert into myentity (createdDate, lastModifiedDate) values ('2020-03-02 16:43:04.581+00', '2020-03-02 16:43:04.581+00')]

然而,当我再次读取它时,它返回的是我在应用程序代码中设置的默认时区:< code >美国/夏威夷,而不是< code>UTC:

[16:43:04.692Z #4c5.043 TRACE -            -   ] o.h.t.d.sql.BasicExtractor: extracted value ([createdD4_0_0_] : [TIMESTAMP]) - [2020-03-02T06:43:04.581]
[16:43:04.692Z #4c5.043 TRACE -            -   ] o.h.t.d.sql.BasicExtractor: extracted value ([lastModi5_0_0_] : [TIMESTAMP]) - [2020-03-02T06:43:04.581]
[16:43:04.695Z #4c5.043 TRACE -            -   ] o.h.l.p.e.p.i.ResultSetProcessorImpl: Done processing result set (1 rows)
[16:43:04.696Z #4c5.043 TRACE -            -   ] o.h.l.p.e.p.i.AbstractRowReader: Total objects hydrated: 1
[16:43:04.696Z #4c5.043 TRACE -            -   ] o.h.l.p.e.p.i.ResultSetProcessingContextImpl: Skipping create subselects because there are fewer than 2 results, so query by key is more efficient.
[16:43:04.696Z #4c5.043 TRACE -            -   ] o.h.r.j.i.ResourceRegistryStandardImpl: Releasing result set [HikariProxyResultSet@622126582 wrapping org.postgresql.jdbc.PgResultSet@3f0764b8]
[16:43:04.696Z #4c5.043 TRACE -            -   ] o.h.r.j.i.ResourceRegistryStandardImpl: Closing result set [HikariProxyResultSet@622126582 wrapping org.postgresql.jdbc.PgResultSet@3f0764b8]
[16:43:04.696Z #4c5.043 TRACE -            -   ] o.h.r.j.i.ResourceRegistryStandardImpl: Releasing statement [HikariProxyPreparedStatement@1612081040 wrapping select myentity0_.createdDate as createdD4_0_0_, myentity0_.lastModifiedDate as lastModi5_0_0_, where myentity0_.id='123']

我试过添加serverTimeZone=UTC

可能相关:https://hibernate.atlassian.net/browse/HHH-13417

任何帮助都非常感谢。

使现代化

根据@midhunmathew的回答,我发现控制应用程序代码中的日期就足以解决这个问题(也从application.yaml中删除time_zone属性):

myEntity.setCreatedDate(LocalDateTime.ofInstant(Instant.now(), ZoneOffset.UTC))
public void setCreatedDate(LocalDateTime createdAt) 
{ 
  this.createdAt = createdAt; 
}

现在,当写入数据库时,日期被“绑定”并作为< code>UTC插入(与原始帖子相比,在原始帖子中,日期被“绑定”为< code>US\Hawaii,但作为< code>UTC插入):

[10:10:21.475Z #065.042 TRACE -            -   ] o.h.t.d.sql.BasicBinder: binding parameter [1] as [TIMESTAMP] - [2020-03-03T10:10:17.400]
[10:10:21.476Z #065.042 TRACE -            -   ] o.h.t.d.sql.BasicBinder: binding parameter [2] as [TIMESTAMP] - [2020-03-03T10:10:17.400]
[HikariProxyPreparedStatement@860888944 wrapping insert into myentity(createdDate, lastModifiedDate) values ('2020-03-03 10:10:17.4-10', '2020-03-03 10:10:17.4-10')]
[10:10:21.479Z #065.042 TRACE -            -   ] 

并且从数据库读取实体时,日期不再读取为US/夏威夷,而是读取为UTC

[10:10:24.527Z #065.043 TRACE -            -   ] o.h.t.d.sql.BasicExtractor: extracted value ([createdD4_0_0_] : [TIMESTAMP]) - [2020-03-03T10:10:17.400]
[10:10:24.527Z #065.043 TRACE -            -   ] o.h.t.d.sql.BasicExtractor: extracted value ([lastModi5_0_0_] : [TIMESTAMP]) - [2020-03-03T10:10:17.400]

共有3个答案

巢星纬
2023-03-14

这应该使其正常工作:

< code > time zone . set default(time zone . gettime zone(" UTC "));

郝玄天
2023-03-14

虽然@midhun向您展示了如何将Instant转换为LocalDateTime的正确方法,但是我将尝试给出一个理解,这样其他人就不会犯同样的错误。

你的问题的答案是no,它对两者都有效。请参阅留档:https://docs.jboss.org/hibernate/orm/5.3/userguide/html_single/Hibernate_User_Guide.html#basic-datetime-time-zone

关于您的问题,您没有提供有关您的数据库(供应商和时区)和模式的信息,因此很难给出更准确的答案。但我觉得您混淆了所有涉及的日期类型。

从数据库的角度来看,每个数据库都以不同的方式处理日期。例如,Postgres总是以UTC格式保存日期(timestamptz),然后在读取时将日期转换为服务器时区。这就是文档所说的:https://www.postgresql.org/docs/9.1/datatype-datetime.html

对于带有时区的时间戳,内部存储的值始终以UTC为单位。具有指定的显式时区的输入值将使用该时区的适当偏移量转换为UTC。如果输入字符串中没有声明时区,则假定它位于系统的时区参数指示的时区内,并使用时区偏移量转换为UTC。当输出带有时区值的时间戳时,它始终从UTC转换为当前时区,并在该时区中显示为本地时间。要查看另一个时区中的时间,请更改时区或使用AT Time ZONE构造(参见第9.9.3节)。

话虽如此,您的 Java 代码中有 LocalDateTime,我将假设您的数据库中有带有时区的时间戳,因为您尝试将其设置为 UTC,并且数据库位于美国/夏威夷时区。

问题是您使用的是不兼容的日期类型-LocalDateTime没有时区信息-并且应用程序和数据库设置为不同的时区,并且没有正确处理!这就是发生的情况:

写本地-

  • 2020-03-03T10:10:17.400-

转换后的日期是您在日志中看到的日期:

插入myentity(createdDate,lastModifiedDate)值('2020-03-02 16:43:04.581 00','2020-02-02 16:43:04.581 00')

插入myentity(createdDate,lastModifiedDate)值(“2020-03-03 10:10:17.4-10”,“2020-04-03 10:10:7.4-10”)

分区读取时间-

  • 2020-03-03T16:10:17.400 0-

您可以在日志中看到数据库转换后的值:

提取的值([createdd 4 _ 0 _ 0 _]:[时间戳]) - [2020-03-02T06:43:04.581]

提取的值([createdd 4 _ 0 _ 0 _]:[时间戳]) - [2020-03-03T10:10:17.400]

请记住,您的数据库位于< code >美国/夏威夷区域。由于数据库丢弃了区域信息,因此不再进行进一步的转换。

如果可能,请将应用程序和数据库设置为同一时区。此外,使用兼容的数据类型。对于时间戳,请使用即时

申屠飞
2023-03-14

我也面临过同样的问题。我的数据库时区是UTC,我的应用程序时区是新加坡。我通过让实体和表都具有UTC格式的日期来解决这个问题,这样它们之间就不需要进行转换。然后我在getter和setter中对代码中的时间戳进行了转换。

因此,您的MyEntity类将在UTC中存储createdAT和lastModifiedAT。

在二传手中,您可以拥有类似的东西

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

在getter中,您可以有如下内容

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

在转换时间时,您可能还必须删除时区属性以及@CreatedDate和@LastModifiedDate注释。

 类似资料:
  • 我有一个包含记录计数器的固定长度流 记录以开头 字符16+9(人形)包含 字符25+9(人形)包含 用填充并向右对齐的所有数字 记录在1898位置以+结尾(记录为长2000个字符) 我的出口代码有什么问题?为什么我总是得零分?

  • 嗨,我已经习惯了用jQuery编写代码,现在想学习用普通的Javascript编写代码。我用这个例子来显示和隐藏一些选项卡,使用的jQuery,工作正常,但是在JavaScript中如何用不同的方式来编写呢? 我试着比较它们的写法有多不同,这样我就能更好地理解它。如有任何关于如何完成此操作的帮助/提示,将不胜感激。 null null

  • 我试图将数据写入Rust中的内存映射文件,但它不会映射指定的文件,因为它声明给定的fd不可用。 我可以在文件系统上看到它,因此它确实以正确的权限存在。我怀疑这是一个bug,或者我没有以正确的方式使用新的IO API。 这是密码

  • 我使用的是iText 7,特别是方法,将HTML转换为PDF。问题是,我真的不希望在我的服务器上创建PDF文件,我希望在内存中完成所有操作,然后将其发送到用户浏览器,以便他们可以下载它。 谁能告诉我一个如何使用这个库的例子,而不是写文件写到MemoryStream,这样我就可以直接把它发送到浏览器? 我一直在寻找例子,我能找到的似乎都是那些涉及文件输出的例子。 我尝试了以下方法,但不断出现一个错误

  • 我有一个场景,服务器端将接收请求并并发运行作业(不同的作业名或相同的作业名具有不同的jobparameters)。它的用法是在并发线程中新建一个作业对象(包括steps/reader/writer),所以我不打算每次将作业方法声明为@bean并新建一个作业。 实际上,在如何将参数传输到像Reader这样的对象上存在差异。如果使用@bean,必须将参数放入例如JobParameters中,以便使用@

  • 我需要一个不可重入的读写锁,因为锁可能由获取它的线程以外的线程释放。(我意识到这一点时,我开始间歇性地获取非法监视器状态异常。) 我不确定不可重入是否是正确的术语。ReentrantLock允许当前持有锁的线程再次获取它。我不想要这种行为,因此我称之为“不可重入”。 上下文是我有一个使用线程池的套接字服务器。每个连接没有一个线程。请求可能由不同的线程处理。客户端连接可能需要在一个请求中锁定,在另一

  • 问题内容: 我试图在Python中使用子进程,以使外部脚本以类似于服务器的方式打开。外部脚本首先加载模型。完成此操作后,它将通过STDIN接受请求,并将处理后的字符串返回给STDOUT。 到目前为止,我已经尝试过 但是,我不能使用 为了通过子过程重复处理input_strings,无论我使用还是,out都将是空的。但是,当我在读取STDOUT之前关闭stdin时,它起作用了,但是这关闭了子进程,这

  • 我对Kafka和Kafka流很陌生,所以请容忍我。我想知道我是否在正确的轨道上。 我正在给一个Kafka主题写信,试图通过rest服务访问数据。在访问原始数据之前,需要对其进行转换。 到目前为止,我拥有的是一个将原始数据写入主题的制作人。 1)现在我想要streams应用程序(应该是一个在容器中运行的jar),它可以将数据转换为我想要的形状。遵循这里的物化视图范式。 1的过度简化版本。) 2)和另