Suporte ao JPA 2.2 para tipos de data/hora do Java 8

Suporte ao JPA 2.2 para tipos de data/hora do Java 8

*1. Visão geral *

A versão JPA 2.2 introduziu oficialmente o suporte para https://www..com/java-8-date-time-intro [Java 8 Date e Time API]. Antes disso, era necessário confiar em uma solução proprietária ou usar a API do conversor JPA.

Neste tutorial,* mostraremos como mapear os vários tipos Java 8 Date e Time *. Vamos nos concentrar especialmente nos que levam em conta as informações de compensação.

*2. Dependências do Maven *

Antes de começarmos, precisamos incluir a API JPA 2.2 no caminho de classe do projeto. Em um projeto baseado em Maven, podemos simplesmente adicionar sua dependência ao nosso arquivo pom.xml:

<dependency>
    <groupId>javax.persistence</groupId>
    <artifactId>javax.persistence-api</artifactId>
    <version>2.2</version>
</dependency>

Além disso, para executar o projeto, precisamos de uma implementação JPA e do driver JDBC do banco de dados com o qual trabalharemos. Neste tutorial, usaremos o EclipseLink e o banco de dados PostgreSQL:

<dependency>
    <groupId>org.eclipse.persistence</groupId>
    <artifactId>eclipselink</artifactId>
    <version>2.7.4-RC1</version>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>org.postgresql</groupId>
    <artifactId>postgresql</artifactId>
    <version>42.2.5</version>
    <scope>runtime</scope>
    <type>bundle</type>
</dependency>

Fique à vontade para conferir as versões mais recentes da JPA API, https://search. maven.org/search?q=g:org.eclipse.persistence%20AND%20a:eclipselink&core=gav[EclipseLink] e https://search.maven.org/search?q=g:org.postgresql%20AND% 20a: postgresql & core = gav [driver JDBC do PostgreSQL] no Maven Central.

Obviamente, podemos usar outros bancos de dados ou implementações JPA como o Hibernate.

===* 3. Suporte do TimeZone *

Podemos trabalhar com qualquer banco de dados, mas primeiro, devemos verificar o suporte para esses tipos padrão de SQL, pois o JDBC 4.2 é baseado em:

  • TIMESTAMP (n) COM Fuso Horário

  • TIMESTAMP (n) SEM FUSO HORÁRIO

  • TIME (n) COM FUSO HORÁRIO

  • TIME (n) SEM FUSO HORÁRIO

Aqui, n é a precisão fracionária de segundos e tem entre 0 e 9 dígitos. _ SEM FUSO HORÁRIO_ é opcional e pode ser omitido. Se WITH TIME ZONE for especificado, o nome do fuso horário ou o deslocamento para UTC serão necessários.

Podemos representar o fuso horário em um desses dois formatos:

  • Nome do fuso horário *Deslocamento do UTC ou a letra Z para o UTC

Para o nosso exemplo, escolhemos o banco de dados PostgreSQL, graças ao seu suporte completo ao tipo SQL TIME WITH TIME ZONE.

Observe que outros bancos de dados podem não suportar esses tipos.

===* 4. Mapeando tipos de data antes do Java 8 *

Antes do Java 8, geralmente era necessário mapear os tipos SQL genéricos TIME, DATE e TIMESTAMP, para as classes java.sql.* _ _Java.sql.Time, java.sql.Date, _ e _java.sql.Timestamp, _ respectivamente, ou para _java.util tipos java.util.Date e java.util.Calendar.

Primeiro, vamos ver como usar os tipos java.sql. Aqui, estamos simplesmente definindo os atributos com os tipos java.sql como parte de uma classe _ @ Entity_:

@Entity
public class JPA22DateTimeEntity {

    private java.sql.Time sqlTime;
    private java.sql.Date sqlDate;
    private java.sql.Timestamp sqlTimestamp;

   //...
}
*Enquanto os tipos _java.sql_ funcionam como qualquer outro tipo sem nenhum mapeamento adicional, os tipos _java.util_ precisam especificar os tipos temporais correspondentes.

Isso é feito através da anotação _ @ Temporal_ cujo atributo value nos permite especificar o tipo JDBC correspondente, usando a enumeração TemporalType:

@Temporal(TemporalType.TIME)
private java.util.Date utilTime;

@Temporal(TemporalType.DATE)
private java.util.Date utilDate;

@Temporal(TemporalType.TIMESTAMP)
private java.util.Date utilTimestamp;

Observe que, se estivermos usando o Hibernate como uma implementação, isso não suporta o mapeamento de Calendar para TIME.

Da mesma forma, podemos usar a classe Calendar:

@Temporal(TemporalType.TIME)
private Calendar calendarTime;

@Temporal(TemporalType.DATE)
private Calendar calendarDate;

@Temporal(TemporalType.TIMESTAMP)
private Calendar calendarTimestamp;

Nenhum desses tipos tem suporte para o fuso horário ou o deslocamento. Para lidar com essas informações, tradicionalmente tínhamos que armazenar a hora UTC.

===* 5. Mapeando tipos de data do Java 8 *

O Java 8 introduziu os pacotes java.time e a API JDBC 4.2 adicionou suporte aos tipos SQL adicionais TIMESTAMP WITH TIME ZONE e TIME WITH TIME ZONE.

*Agora podemos mapear os tipos JDBC _TIME, DATE, _ e _TIMESTAMP_ para os tipos _java.time_* - _LocalTime, _ _LocalDate_ e _LocalDateTime_:
@Column(name = "local_time", columnDefinition = "TIME")
private LocalTime localTime;

@Column(name = "local_date", columnDefinition = "DATE")
private LocalDate localDate;

@Column(name = "local_date_time", columnDefinition = "TIMESTAMP")
private LocalDateTime localDateTime;

Além disso, temos suporte para o fuso horário local de deslocamento para o UTC através das classes OffsetTime e OffsetDateTime:

@Column(name = "offset_time", columnDefinition = "TIME WITH TIME ZONE")
private OffsetTime offsetTime;

@Column(name = "offset_date_time", columnDefinition = "TIMESTAMP WITH TIME ZONE")
private OffsetDateTime offsetDateTime;

Os tipos de colunas mapeados correspondentes devem ser TIME WITH TIME ZONE e TIMESTAMP WITH TIME ZONE. Infelizmente, nem todos os bancos de dados suportam esses dois tipos.

Como podemos ver, o JPA suporta essas cinco classes como tipos básicos e não há informações adicionais necessárias para distinguir entre as informações de data e/ou hora.

Depois de salvar uma nova instância da nossa classe de entidade, podemos verificar se os dados foram inseridos corretamente:

6. Conclusão

Antes do Java 8 e JPA 2.2, os desenvolvedores geralmente tinham que converter tipos de data/hora em UTC antes de persistir. O JPA 2.2 agora suporta esse recurso imediatamente, suportando o deslocamento para o UTC e aproveitando o suporte ao JDBC 4.2 para o fuso horário.

O código fonte completo para esses exemplos pode ser encontrado em over no Github.