2

我正在使用 jooq (v3.11.9) 访问在 UTC 时间运行的 MySQL 数据库。我使用生成的实体并且正在使用 JSR-310 时间类型。我在配置中使用的选项:

<javaTimeTypes>true</javaTimeTypes>

我的理解是 MySQLdatetimetimestamp类型都映射到LocalDateTime这很有意义,因为 MySQL 不会随时间存储时区信息。但是,当我在不同时区(在我的情况下为 EST)的机器上运行查询时,日期都在我的本地机器时区,即使会话时区是 UTC

我已经确认会话时区是 UTC

dslContext.fetch("SELECT @@system_time_zone,  @@global.time_zone, @@session.time_zone;")

返回

|@@system_time_zone|@@global.time_zone|@@session.time_zone|
+------------------+------------------+-------------------+
|UTC               |SYSTEM            |SYSTEM             |
+------------------+------------------+-------------------+

时区转换示例:

dslContext.select(MY_TABLE.EPOCH_DT_TM, MY_TABLE.CREATION_TIMESTAMP).from(MY_TABLE).limit(1).fetch()

+-----------------------+-----------------------+
|epoch_dt_tm            |creation_timestamp     |
+-----------------------+-----------------------+
|2019-04-18T13:57:39.163|2019-09-24T16:06:47.754|
+-----------------------+-----------------------+
// CAST to STRING PROPERLY USES SESSION TIMEZONE 
dslContext.select(MY_TABLE.EPOCH_DT_TM.cast(org.jooq.impl.SQLDataType.VARCHAR(100)), MY_TABLE.CREATION_TIMESTAMP.cast(org.jooq.impl.SQLDataType.VARCHAR(100))).from(MY_TABLE).limit(1).fetch()
+--------------------------+--------------------------+
|cast                      |cast                      |
+--------------------------+--------------------------+
|2019-04-18 17:57:39.163000|2019-09-24 20:06:47.754000|
+--------------------------+--------------------------+

我生成的实体中的字段:

    public final TableField<MyTableRecord, LocalDateTime> EPOCH_DT_TM = createField("epoch_dt_tm", org.jooq.impl.SQLDataType.LOCALDATETIME, this, "");
    public final TableField<MyTableRecord, LocalDateTime> CREATION_TIMESTAMP = createField("creation_timestamp", org.jooq.impl.SQLDataType.LOCALDATETIME.nullable(false).defaultValue(org.jooq.impl.DSL.field("CURRENT_TIMESTAMP(6)", org.jooq.impl.SQLDataType.LOCALDATETIME)), this, "");

所以我的问题是:

  1. 这是预期的行为吗?不应该用表中的原始(非时区)日期填充记录。由于某种原因,日期是否仍在后台转换为 java.sql.Timestamp?

  2. 如果这是预期的行为,是否有任何方法可以确保您在会话时区中获取日期,而不管客户端计算机上的本地时区如何?如果代码的行为取决于机器时区,则很难在本地进行测试。

预先感谢您的帮助。

4

1 回答 1

1

我最近发现,根据所使用的数据库驱动程序,jOOQ 在 DateTime 解析中可能会表现出一些奇怪的行为。 jOOQ 将偏移日期时间返回为 Z (UTC),即使它不是

具体来说,在我的例子中,使用不同的 Postgres 驱动程序导致 DefaultBinding.java 接收到一个具有时间戳的日历对象,但在其上调用 toString 以进行解析。事实证明,toString 没有打印时区,然后 jOOQ 推断它是本地时间。

对我来说,DefaultBinding.java 中的违规行(我使用的是带时区的时间戳)是:

else if (type == OffsetDateTime.class) {
    result = (T) offsetDateTime(ctx.resultSet().getString(ctx.index()));
}

由于没有时区,您可能在该系列 else if 中处于不同的位置。

在我的测试中,我还发现更改系统时间会改变结果,但更改会话时间并没有起到任何作用。

对我来说幸运的是,切换到标准 Postgres 驱动程序解决了这个问题。如果没有,我将考虑重载 OffsetDateTime 的绑定以修复 toString 的使用以及相关时区的相关剥离。不幸的是,您可能需要走这条路,除非您也使用可以升级或替换的 SQL 驱动程序。或者,您可以将其与时区一起存储,然后在从数据库加载时转换为所需的时区。

于 2019-09-24T18:23:00.067 回答