4.1 Excursus en la historia

La tarea de guardar objetos Java en la base de datos fue relevante casi inmediatamente después de la creación del lenguaje Java. En ese momento, solo había un tipo de datos en el lenguaje Java, Fecha, que almacenaba la hora de acuerdo con el estándar de tiempo de UNIX: como el número de milisegundos desde 1970.

Bueno, en las bases de datos en ese momento ya había diferentes tipos de datos para fechas, al menos había tipos separados para fecha, hora y fecha + hora:

  • FECHA
  • TIEMPO
  • MARCA DE TIEMPO

Por lo tanto, los creadores del lenguaje Java le agregaron un paquete especial: java.sql, que contenía clases:

  • java.sql.fecha
  • java.sql.Tiempo
  • java.sql.timestamp

Mapear estas clases es un verdadero placer:


@Entity
public class TemporalValues {
 
	@Basic
    private java.sql.Date sqlDate;
 
	@Basic
    private java.sql.Time sqlTime;
 
    @Basic
    private java.sql.Timestamp sqlTimestamp;
}

Pero dado que los programadores solían trabajar con la clase java.util.Date, Hibernate agregó una anotación especial @Temporalpara controlar el mapeo del tipo Fecha.

Ejemplo:

// If the annotation is missing, then the database will have a TIMESTAMP type
Date dateAsTimestamp;

@Temporal(TemporalType.DATE) // will be mapped to DATE type
Date dateAsDate;

@Temporal(TemporalType.TIME) // will be mapped to TIME type
Date dateAsTime;

El tipo java.util.Calendary java.util.Dateel tipo predeterminado utilizan el tipo TIMESTAMP para representarlos en la base de datos.

4.2 Nuevo tiempo

En la actualidad, con el mapeo, todo es mucho más simple y mejor. Todas las bases de datos admiten 4 tipos de datos para trabajar con el tiempo:

  • FECHA - fecha: año, mes y día.
  • TIEMPO - tiempo: horas, minutos, segundos.
  • TIMESTAMP - fecha, hora y nanosegundos.
  • TIMESTAMP CON ZONA HORARIA - TIMESTAMP y zona horaria (nombre de zona o desplazamiento).

Para representar el tipo FECHAen Java, debe usar una clase java.time.LocalDatede la API DateTime de JDK 8.

TipoTIEMPOde la base de datos se puede representar mediante dos tipos de Java: java.time.LocalTimey java.time.OffsetTime. Nada complicado.

Y la fecha y hora exactas representadas por el tipoMARCA DE TIEMPOen la base, en Java se puede representar por 4 tipos:

  • java.tiempo.instantáneo
  • java.time.LocalDateTime
  • java.time.OffsetDateTime
  • java.time.ZonedDateTime

Y finalmenteMARCA DE TIEMPO CON ZONA HORARIApuede ser representado por dos tipos:

  • java.time.OffsetDateTime
  • java.time.ZonedDateTime

Como ya está familiarizado con la API de DateTime , recordar este asunto no será difícil para usted :)

Mapearlos es puro placer:

@Basic
private java.time.LocalDate localDate;

@Basic
private java.time.LocalTime localTime;

@Basic
private java.time.OffsetTime offsetTime;

@Basic
private java.time.Instant instant;

@Basic
private java.time.LocalDateTime localDateTime;

@Basic
private java.time.OffsetDateTime offsetDateTime;

@Basic
private java.time.ZonedDateTime zonedDateTime;

La anotación @Basicsignifica que el campo debe procesarse automáticamente : Hibernate decidirá en qué columna y tipo se debe mapear este campo.

4.3 Trabajar con zonas horarias

Si la zona horaria es parte de una fecha, almacenarlos en la base de datos es simple, como una fecha normal:

@Basic
private java.time.OffsetDateTime offsetDateTime;

@Basic
private java.time.ZonedDateTime zonedDateTime;

Sin embargo, si desea almacenar las zonas horarias por separado de la fecha:

@Basic
private java.time.TimeZone timeZone;

@Basic
private java.time.ZoneOffset zonedOffset;

Entonces Hibernate los almacenará en tipo VARCHAR por defecto. Lo cual, de hecho, es lógico, ya que TimeZone generalmente tiene un nombre de cadena como "UTC + 3" o "Cairo".

4.4 Configuración de su propia zona horaria

Cuando trabaje guardando fechas en la base de datos, encontrará el hecho de que ya hay 4 lugares donde puede configurar la zona horaria actual:

  • sistema operativo del servidor;
  • SGBD;
  • aplicación Java
  • Hibernar.

Si el DBMS no especifica una zona horaria (TimeZone), la tomará de la configuración del sistema operativo. Esto puede ser un inconveniente, ya que los DBMS de respaldo a menudo se encuentran en otros centros de datos que tienen su propia zona horaria.

Por lo tanto, casi todos los administradores de DBMS configuran una sola zona para que los datos se puedan transferir fácilmente de un servidor a otro.

La situación es similar con una aplicación Java. También se puede ejecutar en diferentes servidores en diferentes centros de datos, por lo que generalmente se le asigna explícitamente una zona horaria.


java -Duser.timezone=UTC ...

O mientras el programa se está ejecutando:

TimeZone.setDefault(TimeZone.getTimeZone("UTC"));

Y, por supuesto, Hibernate le permite configurar su zona horaria explícitamente.

Primero, se puede especificar al configurar SessionFactory:

settings.put(
    AvailableSettings.JDBC_TIME_ZONE,
    TimeZone.getTimeZone("UTC")
);

En segundo lugar, se puede especificar la zona horariapara una sesión específica:

Session session = sessionFactory()
    .withOptions()
    .jdbcTimeZone(TimeZone.getTimeZone("UTC"))
    .openSession();

4.5 Anotación @TimeZoneStorage

A menudo sucede que los programadores comenzaron a diseñar una base de datos basada en trabajar en un país (y una zona horaria), y luego de un par de años necesitaron agregar soporte para trabajar en diferentes zonas horarias.

Por lo tanto, simplemente agregaron una columna separada a la base de datos para almacenar la zona horaria. Esta es una situación tan común que Hibernate ha agregado una anotación especial que le permite almacenar la zona horaria de una fecha específica en una columna separada.

Ejemplo:

@TimeZoneStorage(TimeZoneStorageType.COLUMN)
@TimeZoneColumn(name = "birthday_offset_offset")
@Column(name = "birthday_offset")
private OffsetDateTime offsetDateTimeColumn;

@TimeZoneStorage(TimeZoneStorageType.COLUMN)
@TimeZoneColumn(name = "birthday_zoned_offset")
@Column(name = "birthday_zoned")
private ZonedDateTime zonedDateTimeColumn;

Esta es una muleta. Pero también hay una excusa para ello: apareció en un momento en el que aún no existía la API DateTime. Y era imposible almacenar TimeZone en la clase java.util.Date.

Realmente espero que no veas esto a menudo en tu código.