如何在 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
但奇怪的是不需要两种更常用的类型 Instant
和 ZonedDateTime
。如果你的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
数字;不需要 BigDecimal
或 BigInteger
。我想您可能能够为这两个数字中的至少一个使用 32 位整数列。但我会选择 64 位整数列类型,以简单起见并与 java.time.Instant
class' 对 longs 直接兼容。
long seconds = instant.getEpochSecond() ;
long nanos = instant.getNano() ;
……和……
Instant instant = Instant.ofEpochSecond( seconds , nanos ) ;
按时间顺序排序时,您需要进行多级排序,首先对整个秒列进行排序,然后对纳秒分数列进行二次排序。
对于 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
但奇怪的是不需要两种更常用的类型 Instant
和 ZonedDateTime
。如果你的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
数字;不需要 BigDecimal
或 BigInteger
。我想您可能能够为这两个数字中的至少一个使用 32 位整数列。但我会选择 64 位整数列类型,以简单起见并与 java.time.Instant
class' 对 longs 直接兼容。
long seconds = instant.getEpochSecond() ;
long nanos = instant.getNano() ;
……和……
Instant instant = Instant.ofEpochSecond( seconds , nanos ) ;
按时间顺序排序时,您需要进行多级排序,首先对整个秒列进行排序,然后对纳秒分数列进行二次排序。