如何在 jOOQ 中将 SQL 时间戳与 java Instant 一起使用?

问题描述

我想使用 Instant 类型将其放入 MysqL 数据库timestamp 字段)。不幸的是,在使用 POJO 和 Record#from() 时,出于某种原因,jOOQ 不允许我这样做。我的 gradle 配置如下:

forcedTypes {
    forcedType {
        name = "Instant"
        types = "timestamp"
    }
}

代码正在正确生成,但不起作用并在运行时出现错误

 Data truncation: Incorrect datetime value: '2021-01-16 05:01:25.457+00:00' for column `test`.`messages`.`time_sent` at row 1

我尝试添加自己的转换器和活页夹,但如果在 gradle 配置中设置了 name,它们似乎不起作用,因为在构建过程中会出现警告。但是没有 name 我无法让 jOOQ 为 Instant 字段生成 timestamp

在使用 jOOQ 的 POJO 时,有没有办法将 Instantsql timestamp 一起使用?

解决方法

MySQL 的时间戳数据类型有点愚蠢。

它将时间存储为自纪元(1970 年 1 月 1 日,UTC,午夜)以来的秒数,以 32 位整数表示。这有几个问题:

  • 没有毫秒的空间。您的 Instant 有 .457 毫秒,这需要删除,这就是 JOOQ 拒绝这样做的原因;那是在破坏数据。我假设您不关心这些毫秒,但 JOOQ 不知道这一点。您可以尝试从瞬间中去除毫秒,如果必须,JOOQ 大概会允许保存 Instant,如果该 Instant 具有它所环绕的纪元可被 1000 整除(没有毫秒部分)的属性。除此之外,这很烦人,所以在某些时候,即使它存储为 32 位秒自纪元以来,数据类型现在还单独包含小数秒,从 0 到 999999。要么你有一个旧版本的 MySQL或者底层的表结构不允许这样做,或者JOOQ不知道MySQL至少支持millis。
  • 在 2038-01-19 03:14:07 UTC,您的应用爆炸了。而已。这是 MySQL TIMESTAMP 对象可表示的最后一个时间戳。我们离这还不到 18 年。因此,此数据类型实际上无法使用,您应该使用其他类型。 (这听起来对您来说可能不可信。如果您需要一些令人信服的内容,请直接从源头上仔细阅读 MySQL8's user manual §11.2.2)。 Java 的核心即时存储系统不会受到可怕的 Y2K38 的影响,因为 Java 在 64 位中使用了millis-since-epoch;在数字用完之前,我们还有几十亿年的时间。
  • 请注意,打印的消息有点误导。瞬间存储为自纪元以来的毫秒数,并且没有时区,打印输出中的 +00.00 表明确实如此。它没有 - 因此事实上 mysql 的 TIMESTAMP 类型也不是问题。

解决方案

到目前为止,最好的解决方案是使用没有像这样损坏的数据库引擎。例如,试试 postgres。

一个遥远的第二个解决方案是仔细阅读 JOOQ issue #9665 那里@lucaseder(JOOQ 的核心贡献者;他是在那里施展魔法的天才:P)说它还没有工作,但是那里有一些代码你可以可以使用。

请注意,如果您真的关心区域,这会变得更加复杂。有 3 种不同的方式来表示时间;这些是不同的,如果没有额外的信息和警告,就不能相互转换;无论如何,大多数工具都会错误地默默地转换它们,当这种情况发生时,痛苦就会随之而来。因此,您可能需要考虑一下您在这里拥有的 3 种时间中的哪一种:

  • solarflares time:某事发生或将要发生的时刻,例如太阳耀斑。如果某个政治实体决定更改时区,则无效。无论时区发生什么,“事件发生前的时间”/“事件发生后的时间”每秒都会增加 1 秒。在 Java 术语中,java.time.Instant
  • 预约时间:我打电话给我在阿姆斯特丹的理发师,预约时间为 2023 年 1 月 5 日 14:00。你会认为这就像太阳耀斑时间,但是,不。如果荷兰议会猛烈抨击并通过一项法律,荷兰将不再遵守夏令时并保留在夏季,那么当木槌落下的那一刻,距离我的约会的秒数增加了一个小时(是吗?下降一个小时?)。这一点都不奇怪 - 请参阅欧盟指令 2000/84/EC - 事实上很有可能。 Solarflares 时间不应该像这样变化,约会时间确实如此。最好表示为年 + 月 + 日 + 小时 + 分钟 + 秒 + 毫秒 + 一个完整的区域(这是 Europe/Amsterdam,而不是像 +0800PST 这样愚蠢无用的东西)。在 Java 术语中,ZonedDateTime
  • 闹钟时间:只有年、月、日、时、分、秒。就是这样 - 它不代表任何特别的东西,而只是概念。如果我将闹钟设置为早上 08 点叫醒我,然后穿越太平洋旅行,那么随着时区的移动,闹钟响起的时间应该会发生巨大变化。在 Java 术语中,LocalDateTime 和朋友。

我假设您有一个 solarflares 时间(例如,要跟踪“用户 X 在此时更改其密码”)。这个答案假设你是这个意思。如果你没有,我的建议会有所改变。大多数情况下,“不要使用 mysql”这件事会变得更加强大:您真正想要的是数据类型 TIMESTAMP WITH TIME ZONE,例如 postgres has

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...