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.