sql >> Base de Datos >  >> RDS >> Mysql

Cómo almacenar un Java Instant en una base de datos MySQL

Truncar a microsegundos

Obviamente no podemos exprimir los nanosegundos resolución de un Instant en los microsegundos resolución de los tipos de datos MySQL DateTime y Timestamp .

Si bien no uso MySQL, imagino el controlador JDBC está diseñado para ignorar los nanosegundos al recibir un Instant , truncando el valor a microsegundos. Le sugiero que pruebe un experimento para ver, y quizás examine el código fuente de su controlador que cumple con JDBC 4.2 y posterior.

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 ) ;

…y…

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

Las especificaciones JDBC 4.2 requiere soporte para OffsetDateTime pero curiosamente no requiere los dos tipos más utilizados, Instant y ZonedDateTime . Si su controlador JDBC no es compatible con Instant , convertir.

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`. 

Luego compare.

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

Sabiamente, le preocupa que los valores extraídos de la base de datos no coincidan con el valor original. Una solución, si es aceptable para su problema comercial, es truncar los nanosegundos a microsegundos en sus datos originales. Recomiendo este enfoque en general.

El java.tiempo las clases ofrecen un truncatedTo método. Pase una ChronoUnit objeto enum para especificar la granularidad. En este caso, sería ChronoUnit.MICROS .

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

Actualmente, este enfoque debería ser suficiente, ya que es poco probable que tenga nanosegundos en sus datos. Las computadoras convencionales de hoy en día no cuentan con relojes de hardware capaces de capturar nanosegundos, que yo sepa.

Cuenta desde la época

Si no puede permitirse el lujo de perder los datos de nanosegundos que puedan estar presentes, use un conteo desde la época.

Por lo general, recomiendo no rastrear la fecha y la hora como un conteo a partir de una fecha de referencia de época. Pero tiene pocas opciones para almacenar sus valores basados ​​en nanosegundos en una base de datos como MySQL y Postgres limitada a valores basados ​​en microsegundos.

Almacenar par de enteros

En lugar de usar la cantidad extremadamente grande de nanosegundos desde una época como 1970-01-01T00:00Z, sugiero seguir el enfoque adoptado por las partes internas del Instant clase:Usa un par de números.

Almacenar un número de enteros segundos como un número entero en su base de datos. En una segunda columna, almacene como un número entero el número de nanosegundos en la fracción de segundo.

Puede extraer/inyectar fácilmente estos números desde/hacia un Instant objeto. Solo long simple de 64 bits los números están involucrados; no es necesario BigDecimal o BigInteger . Supongo que podría usar una columna de enteros de 32 bits para al menos uno de los dos números. Pero elegiría tipos de columna de enteros de 64 bits por simplicidad y por compatibilidad directa con java.time.Instant par de largos de la clase.

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

…y…

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

Al ordenar cronológicamente, deberá realizar una ordenación de niveles múltiples, ordenando primero en la columna de segundos completos y luego ordenando en segundo lugar en la columna de fracción de segundo de nanos.