Saltar al contenido

JPA que almacena OffsetDateTime con ZoneOffset

Mantén la atención ya que en este post vas a hallar el hallazgo que buscas.Este post fue analizado por nuestros especialistas para garantizar la calidad y exactitud de nuestro post.

Solución:

// Editar: Actualicé la respuesta para reflejar las diferencias entre la versión 2.1 y 2.2 de JPA.

// Edición 2: enlace de especificación JPA 2.2 agregado


El problema con JPA 2.1

JPA v2.1 no conoce los tipos de Java 8 e intentará secuenciar el valor proporcionado. Para LocalDateTime, Instant y OffsetDateTime usará el método toString () y guardará el correspondiente string al campo de destino.

Dicho esto, debe decirle a JPA cómo convertir su valor a un tipo de base de datos correspondiente, como java.sql.Date o java.sql.Timestamp.

Implementar y registrar el AttributeConverter interfaz para que esto funcione.

Ver:

  • https://stuetzpunkt.wordpress.com/2015/03/15/persisting-localdate-with-jpa-2-1/
  • http://www.adam-bien.com/roller/abien/entry/new_java_8_date_and

Tenga cuidado con la implementación errónea de Adam Bien: LocalDate debe dividirse en zonas primero.

Usando JPA 2.2

Simplemente no cree el attribute convertidores. Ya están incluidos.

// Actualización 2:

Puede ver esto en las especificaciones aquí: JPA 2.2 spec. Desplácese hasta la última página para ver que se incluyen los tipos de tiempo.

Si usa expresiones jpql, asegúrese de usar el objeto Instant y también use Instant en sus clases PDO.

p.ej

// query does not make any sense, probably.
query.setParameter("createdOnBefore", Instant.now());

Esto funciona bien.

Utilizando java.time.Instant en lugar de otros formatos

De todos modos, incluso si tiene un ZonedDateTime o OffsetDateTime, el resultado leído de la base de datos siempre será UTC, porque la base de datos almacena un instante en el tiempo, independientemente de la zona horaria. La zona horaria es en realidad solo información de visualización (metadatos).

Por lo tanto, recomiendo usar Instant en su lugar, y conviértalo en clases Zoned o Offset Time solo cuando sea necesario. Para restaurar la hora en la zona o el desplazamiento dados, almacene la zona o el desplazamiento por separado en su propio campo de base de datos.

Las comparaciones de JPQL funcionarán con esta solución, solo siga trabajando con instantes todo el tiempo.

PD: Recientemente hablé con algunos chicos de Spring, y también estuvieron de acuerdo en que nunca persistes nada más que un Instant. Solo un instante es un punto específico en el tiempo, que luego se puede convertir usando metadatos.

Usando un valor compuesto

De acuerdo con la especificación JPA 2.2, los valores compuestos no se mencionan. Esto significa que no se incluyeron en la especificación y no puede conservar un solo campo en varias columnas de la base de datos en este momento. Busque “Compuesto” y vea solo las menciones relacionadas con los ID.

Sin embargo, Hibernate podría ser capaz de hacer esto, como se menciona en los comentarios de esta respuesta.

Implementación de ejemplo

Creé este ejemplo con este principio en mente: Abierto para extensión, cerrado para modificación. Lea más sobre este principio aquí: Principio abierto / cerrado en Wikipedia.

Esto significa que puede mantener sus campos actuales en la base de datos (marca de tiempo) y solo necesita agregar una columna adicional, lo que no debería doler.

Además, su entidad puede mantener los establecedores y captadores de OffsetDateTime. La estructura interna no debería preocupar a las personas que llaman. Esto significa que esta propuesta no debería dañar su API en absoluto.

Una implementación podría verse así:

@Entity
public class UserPdo 

  @Column(name = "created_on")
  private Instant createdOn;

  @Column(name = "display_offset")
  private int offset;

  public void setCreatedOn(final Instant newInstant) 
    this.createdOn = newInstant;
    this.offset = 0;
  

  public void setCreatedOn(final OffsetDateTime dt) 
    this.createdOn = dt.toInstant();
    this.offset = dt.getOffset().getTotalSeconds();
  

  // derived display value
  public OffsetDateTime getCreatedOnOnOffset() 
    ZoneOffset zoneOffset = ZoneOffset.ofTotalSeconds(this.offset);
    return this.createdOn.atOffset(zoneOffset);
  

No almacenar Instant en la base de datos, use OffsetDateTime.

Almacene siempre UTC en la base de datos.

OffsetDateTime agrega el desplazamiento de UTC / Greenwich, que Instant ¡no!

Y no es necesario agregar dos columnas si db admite “TIMESTAMP WITH TIME ZONE”.

Para uso de jpa

@Column(name = "timestamp", columnDefinition = "TIMESTAMP WITH TIME ZONE")

Con OffsetDateTime es fácil de convertir a usuarios LocalDateTime después porque conoce el desplazamiento de UTC obtenido por OffsetDateTime.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *