如何在 MySQL 数据库中存储 Java Instant

How to store a Java Instant in a MySQL database

对于 Java Date 对象,最简单的方法是将它们存储为 MySql DateTime 对象(UTC)。切换到 Instant 这种方法将不再有效,因为 MySQL DateTime 不提供存储纳秒的精度。只是截断它们可能会导致新创建的 Instant 对象与从数据库中读取的对象之间的意外比较结果。

BigDecimal 时间戳并不是一个优雅的解决方案:手动编写 select 查询变得更加困难,因为您必须在任何地方转换时间戳以使其可读,并且 select 中的处理=27=] 与 Instant 甚至 Long 值相比有些笨拙。

去这里最好的方法是什么?应该不是varchar吧?

截断到微秒

显然我们不能挤压nanoseconds resolution of an Instant into the microseconds resolution of the MySQL data types DateTime and Timestamp

虽然我不使用 MySQL,但我想 JDBC driver 是为了在接收 Instant 时忽略纳秒,将值截断为微秒。我建议您尝试一个实验,看看并检查您的驱动程序的源代码是否符合 JDBC 4.2 及更高版本。

Instant instant = Instant.now().with( ChronoField.NANO_OF_SECOND , 123_456_789L ) ;  //Set the fractional second to a spefic number of nanoseconds.
myPreparedStatement.setObject( … , instant ) ;

……和……

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

JDBC 4.2 规范 需要支持 OffsetDateTime 但奇怪的是不需要两种更常用的类型 InstantZonedDateTime。如果你的JDBC driver不支持Instant,请转换。

OffsetDateTime odt = myResultSet.getObject( … , OffsetDateTime.class ) ;  // Use `OffsetDateTime` if your JDBC driver does not support `Instant`. 
Instant instant2 = odt.toInstant() ;  // Convert from `OffsetDateTime` to `Instant`. 

然后比较。

Boolean result = instant.equals( instant2 ) ;
System.out.println( "instant: " + instant + " equals instant2: = " + instant2 + " is: " + result ) ;

您明智地担心从数据库中提取的值与原始值不匹配。如果您的业务问题可以接受,一种解决方案是将原始数据中的任何纳秒截断为微秒。我一般推荐这种方法。

java.time classes 提供 truncatedTo method. Pass a ChronoUnit enum object to specify the granularity. In this case, that would be ChronoUnit.MICROS.

Instant instant = Instant().now().truncatedTo( ChronoUnit.MICROS ) ;

目前这种方法应该足够了,因为您的数据中不太可能有任何纳秒。据我所知,当今的主流计算机不支持能够捕获纳秒的硬件时钟。

从纪元开始计数

如果您无法承受丢失可能存在的任何纳秒级数据,请使用纪元计数。

我通常建议不要将日期时间作为从纪元参考日期开始的计数。但是在将基于纳秒的值存储在数据库中时,您几乎没有其他选择,例如 MySQL 而 Postgres 仅限于基于微秒的值。

存储整数对

与其使用自 1970-01-01T00:00Z 等纪元以来的极大量纳秒,我建议遵循 Instant class 内部采用的方法:使用数字。

秒作为整数存储在您的数据库中。在第二列中,以整数形式存储小数秒中的纳秒数。

您可以轻松地 extract/inject 这些数字 from/to 一个 Instant 对象。仅涉及简单的 64 位 long 数字;不需要 BigDecimalBigInteger。我想您可能能够为这两个数字中的至少一个使用 32 位整数列。但我会选择 64 位整数列类型,以简单起见并与 java.time.Instant class' 对 longs 直接兼容。

long seconds = instant.getEpochSecond() ;
long nanos = instant.getNano() ;

……和……

Instant instant = Instant.ofEpochSecond( seconds , nanos ) ;

按时间顺序排序时,您需要进行多级排序,首先对整个秒列进行排序,然后对纳秒分数列进行二次排序。