【问题标题】:JDBC MySQL reading and writing TIMESTAMP as milliseconds since Unix Epoch自 Unix Epoch 以来,JDBC MySQL 以毫秒为单位读取和写入 TIMESTAMP
【发布时间】:2016-04-12 21:19:03
【问题描述】:

我在通过 JDBC 访问的 MySQL 表中有一个 TIMESTAMP 列。在 Java 方面,我使用的是 JodaTime。

我想将我所有的瞬间表示为自 Unix 纪元以来的毫秒数。我只想使用一个整数字段,但我想使用 ON UPDATE CURRENT_TIMESTAMP 语法,它只支持 TIMESTAMP/DATETIME 类型。

JodaTime 让我可以轻松地在不同的表示和自纪元以来的毫秒之间进行转换,但 it's not so simple to use milliseconds since epoch with JDBC/MYSQL

有什么方法可以让我使用 JDBC 将 milliseconds_since_epoch 存储在 TIMESTAMP 列中,并将 TIMESTAMP 列检索为 milliseconds_since_epoch 而不必担心由于客户端或服务器更改时区而导致的值发生变化。

我宁愿不必弄乱 mysql 服务器设置或 jdbc 连接设置,但如果这是唯一的方法,我愿意。

【问题讨论】:

  • 您能否详细说明为什么您不想使用 bigint 而实际上存储毫秒而不是定位日期/时间转换?如果只是 TIMESTAMP 列更新的快,您可以定义一个匹配触发器。首先,您依靠 MySQL 自动更新列,但是您需要一种方法来设置它……它是什么?
  • 我开始认为使用 bigint +trigger 是我最好的选择,但我仍然保留这个问题以防万一有什么好方法。如果没有通过 JDBC 设置字段的方法,那可能没问题,因为我认为我不需要对同一列进行设置+自动更新,但如果时间戳字段可以同时满足这两个目的,那就太好了。
  • 您可能还想考虑选择/报告:日期列提供了一些非常好的嵌入式功能,而秒/毫秒在按同一工作日分组时不是很方便......

标签: java mysql jdbc timestamp


【解决方案1】:

有什么方法可以让我使用 JDBC 将 milliseconds_since_epoch 存储在 TIMESTAMP 列中,并将 TIMESTAMP 列检索为 milliseconds_since_epoch

对于 5.6.4 之前的 MySQL 服务器版本:

没有。 TIMESTAMP 列将丢弃小数秒(参考:here)。

如果您确实需要存储毫秒,则必须将它们放在单独的数字列中。但是,如果您可以使用整秒的时间分辨率,那么 TIMESTAMP 列将在存储值时自动将值转换为 UTC(参考:here)。

对于 MySQL 服务器版本 5.6.4 及更高版本:

是的。有关详细信息,请参阅以下 MySQL 文档主题:

Fractional Seconds in Time Values

【讨论】:

  • 随着 MySQL 5.7 的发展:dev.mysql.com/doc/refman/5.7/en/datetime.html
  • 其实 MySQL 5.6.4 加入了支持。 5.6.4 之前的 MySQL 不支持它,但 5.6.4 或更高版本(包括所有 5.7)确实支持时间值的小数秒。
【解决方案2】:

让您的工具担心毫秒数

您正在与 SQL、JDBC 和 Java 的类型系统作斗争。不要担心毫秒。您的数据库、驱动程序和 Java 已经在为您执行此操作,但更精细(MySQL 中为微秒,Java 中为纳秒)。

我想将我所有的瞬间表示为自 Unix 纪元以来的毫秒数。

这正是 MySQL 在其 TIMESTAMP 数据类型中为您所做的,只是更精细 - 自 1970 年开始以 UTC 为单位的微秒计数。致quote the doc

TIMESTAMP 的范围为 '1970-01-01 00:00:01' UTC 到 '2038-01-19 03:14:07' UTC。

TIMESTAMP 值可以包括一个尾随小数秒部分,精度高达微秒(6 位)

您对留在 UTC 的担忧……

将 TIMESTAMP 列检索为 milliseconds_since_epoch,而不必担心由于客户端或服务器更改时区而导致的值更改

……已经解决了。 MySQL 以 UTC 格式存储 TIMESTAMP,您的 JDBC 驱动程序应该以 UTC 格式检索该值,而 java.time 类 Instant 以 UTC 格式存储该值。 Instant 类代表UTC 时间线上的时刻,分辨率为nanoseconds(最多九 (9) 位小数)。 所以您一直只有 UTC。

尽可能避免使用麻烦的旧日期时间类。而是使用它们的替代品 java.time 类。如果您的 JDBC driver 更新为 JDBC 4.2 及更高版本,您可以直接使用 java.time 类型。

Instant instant = Instant.now() ;  // Current moment in UTC with resolution up to nanoseconds.
myPreparedStatement.setObject( … , instant ) ;

……和……

Instant instant = myResultSet.getObject( … , Instant.class ) ;

如果您的 JDBC 驱动程序尚不兼容,请暂时使用旧的 java.sql 类型,但立即与 java.time 进行转换。不要使用 java.sql 类型执行业务逻辑。

myPreparedStatement.setTimestamp( … , java.sql.Timestamp.from( instant ) ) ;

……和……

Instant instant = myResultSet.getTimestamp( … ).toInstant() ;

当您想要调整 UTC 并进入时区(例如向用户展示)时,应用 ZoneId 以获取 ZonedDateTime。搜索 Stack Overflow 以获取更多示例。

ZoneId z = ZoneId.of( "America/Montreal" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;  // Same moment, different wall-clock time.

如果你坚持访问毫秒以来的纪元,请询问Instant 对象。但要注意数据丢失,因为该时刻可能会保存来自 MySQL 的微秒或来自其他来源的纳秒。要求毫秒意味着截断更小的一秒。

long millisSinceEpoch = instant.toEpochMilli() ;

往另一个方向发展。

Instant instant = Instant.ofEpochMilli( millisSinceEpoch ) ;

关于java.time

java.time 框架内置于 Java 8 及更高版本中。这些类取代了麻烦的旧 legacy 日期时间类,例如 java.util.DateCalendarSimpleDateFormat

Joda-Time 项目现在位于maintenance mode,建议迁移到java.time 类。

要了解更多信息,请参阅Oracle Tutorial。并在 Stack Overflow 上搜索许多示例和解释。规格为JSR 310

您可以直接与您的数据库交换 java.time 对象。使用符合JDBC 4.2 或更高版本的JDBC driver。不需要字符串,不需要java.sql.* 类。

从哪里获得 java.time 类?

  • Java SE 8Java SE 9 及更高版本
    • 内置。
    • 标准 Java API 的一部分,带有捆绑实现。
    • Java 9 添加了一些小功能和修复。
  • Java SE 6Java SE 7
    • 大部分 java.time 功能都在ThreeTen-Backport 中向后移植到 Java 6 和 7。
  • Android
    • java.time 类的 Android 捆绑包实现的更高版本。
    • 对于早期的 Android (ThreeTenABP 项目采用 ThreeTen-Backport(如上所述)。见How to use ThreeTenABP…

ThreeTen-Extra 项目通过附加类扩展了 java.time。该项目是未来可能添加到 java.time 的试验场。您可以在这里找到一些有用的类,例如IntervalYearWeekYearQuartermore

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-07-07
    • 1970-01-01
    • 2011-01-28
    • 1970-01-01
    相关资源
    最近更新 更多