4.1 Excurso na história

A tarefa de salvar objetos Java no banco de dados tornou-se relevante quase imediatamente após a criação da linguagem Java. Naquela época, havia apenas um tipo de dados na linguagem Java, Date, que armazenava a hora de acordo com o padrão de tempo do UNIX: como o número de milissegundos desde 1970.

Bem, nos bancos de dados daquela época já existiam diferentes tipos de dados para datas, pelo menos existiam tipos separados para data, hora e data + hora:

  • DATA
  • TEMPO
  • TIMESTAMP

Portanto, os criadores da linguagem Java adicionaram um pacote especial a ela - java.sql, que continha as classes:

  • java.sql.date
  • java.sql.Time
  • java.sql.timestamp

Mapear essas classes é um verdadeiro prazer:


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

Mas como os programadores costumavam trabalhar com a classe java.util.Date, o Hibernate adicionou uma anotação especial @Temporalpara controlar o mapeamento do tipo Date.

Exemplo:

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

O tipo java.util.Calendare java.util.Dateo tipo padrão usam o tipo TIMESTAMP para representá-los no banco de dados.

4.2 Novo horário

Atualmente, com o mapeamento, tudo é muito mais simples e melhor. Todos os bancos de dados suportam 4 tipos de dados para trabalhar com o tempo:

  • DATA - data: ano, mês e dia.
  • TIME - tempo: horas, minutos, segundos.
  • TIMESTAMP - data, hora e nanossegundos.
  • TIMESTAMP WITH TIME ZONE - TIMESTAMP e fuso horário (nome da zona ou deslocamento).

Para representar o tipo DATAem Java, você precisa usar uma classe java.time.LocalDateda API JDK 8 DateTime.

TipoTEMPOdo banco de dados podem ser representados por dois tipos de Java: java.time.LocalTimee java.time.OffsetTime. Nada complicado.

E a data e hora exatas representadas pelo tipoTIMESTAMPna base, em Java pode ser representado por 4 tipos:

  • java.time.Instant
  • java.time.LocalDateTime
  • java.time.OffsetDateTime
  • java.time.ZonedDateTime

E finalmenteTIMESTAMP COM FUSO HORÁRIOpode ser representado por dois tipos:

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

Como você já está familiarizado com a API DateTime , lembrar desse assunto não será difícil para você :)

Mapeá-los é puro prazer:

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

A anotação @Basicsignifica que o campo deve ser processado automaticamente : o Hibernate decidirá em qual coluna e tipo este campo deve ser mapeado.

4.3 Trabalhando com fusos horários

Se o fuso horário fizer parte de uma data, armazená-los no banco de dados é simples - como uma data normal:

@Basic
private java.time.OffsetDateTime offsetDateTime;

@Basic
private java.time.ZonedDateTime zonedDateTime;

No entanto, se você quiser armazenar os fusos horários separadamente da data:

@Basic
private java.time.TimeZone timeZone;

@Basic
private java.time.ZoneOffset zonedOffset;

Então o Hibernate irá armazená-los no tipo VARCHAR por padrão. O que, de fato, é lógico, já que o TimeZone geralmente possui um nome de string como "UTC + 3" ou "Cairo".

4.4 Definindo seu próprio fuso horário

Ao trabalhar com o salvamento de datas no banco de dados, você se deparará com o fato de que já existem 4 locais onde você pode definir o fuso horário atual:

  • Sistema operacional do servidor;
  • SGBD;
  • aplicativo Java
  • Hibernar.

Se o DBMS não especificar um fuso horário (TimeZone), ele será obtido nas configurações do sistema operacional. Isso pode ser inconveniente, pois os DBMSs de backup geralmente estão localizados em outros datacenters que possuem seu próprio fuso horário.

Portanto, quase todos os administradores de DBMS definem uma única zona para que os dados possam ser facilmente transferidos de um servidor para outro.

A situação é semelhante com um aplicativo Java. Ele também pode ser executado em diferentes servidores em diferentes data centers, portanto, geralmente possui um fuso horário explícito.


java -Duser.timezone=UTC ...

Ou durante a execução do programa:

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

E, claro, o Hibernate permite que você defina seu fuso horário explicitamente.

Primeiro, pode ser especificado ao configurar o SessionFactory:

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

Em segundo lugar, o fuso horário pode ser especificadopara uma sessão específica:

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

4.5 Anotação @TimeZoneStorage

Muitas vezes acontece que os programadores começaram a projetar um banco de dados com base no trabalho em um país (e um fuso horário) e, depois de alguns anos, precisaram adicionar suporte para trabalhar em diferentes fusos horários.

Portanto, eles simplesmente adicionaram uma coluna separada ao banco de dados para armazenar o fuso horário. Esta é uma situação tão comum que o Hibernate adicionou uma anotação especial que permite armazenar o TimeZone de uma data específica em uma coluna separada.

Exemplo:

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

Isso é uma muleta. Mas também há uma desculpa para isso: surgiu em uma época em que a API DateTime ainda não existia. E era impossível armazenar TimeZone na classe java.util.Date.

Eu realmente espero que você não veja isso com frequência em seu código.