As novas classes da API de data do java 8 (LocalDate, LocalDateTime, YearMonth, etc) não possuem suporte padrão no mapeamento Hibernate 4/JPA 2.1 que seguem a especificação do Java EE 7, o que não está errado, pois o java EE 7 mantém a compatibilidade com a JDK7, com isso há algumas alternativas para utilizar essas classes do Java 8 nos mapeamentos de entidades nesse cenário.
Java EE 7
No Java EE é possível criando um converter implementando attributeConverter do javax.persistence, assim toda vez que utilizado um atributo LocalDate na entidade será feito a conversão automaticamente para um objeto java.sql.Date, que o tipo que o Hibernate 4 sabe salvar corretamente no banco de dados.
A implementação da interface AttributeConverter é bem simples, apenas sobrescrever dois métodos o convertToDatabaseColumn e convertToEntityAttribute, os quais servirão de converters do LocalDate para java.sql.Date e de java.sql.Date para LocalDate.
import javax.persistence.AttributeConverter; import javax.persistence.Converter; import java.sql.Date; import java.time.LocalDate; @Converter(autoApply = true) public class LocalDatePersistenceConverter implements AttributeConverter { @Override public Date convertToDatabaseColumn(LocalDate entityValue) { if(entityValue != null) { return Date.valueOf(entityValue); } return null; } @Override public LocalDate convertToEntityAttribute(Date databaseValue) { if(databaseValue != null) { return databaseValue.toLocalDate(); } return null; } }
- @Converter(autoApply = true): Ativa o converter para ser acionado em todos atributos mapeados.
Com a criação do converter a utilização fica normal como qualquer outro atributo.
@NotNull @Column(name = "data_cadastro") private LocalDate dataCadastro = LocalDate.now();
Spring 4
Já no Spring tem um conjunto de converter prontos na dependência do data-jpa.
É preciso declarar a dependência do spring-data-jpa.
<dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-jpa</artifactId> <version>1.10.1.RELEASE</version> </dependency>
Na parte de configuração, adicionar o pacote de convertes do Spring nos pacotes escaneados pela aplicação.
@Configuration public class AppConfig { @Bean(name = "entityManagerFactory") public LocalContainerEntityManagerFactoryBean entityManagerFactory(DriverManagerDataSource dataSource) { final LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean(); entityManagerFactoryBean.setDataSource(dataSource); entityManagerFactoryBean.setPackagesToScan("br.com.emmanuelneri.app.model", "org.springframework.data.jpa.convert.threeten"); entityManagerFactoryBean.setLoadTimeWeaver(new InstrumentationLoadTimeWeaver()); entityManagerFactoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter()); return entityManagerFactoryBean; }
Na linha 9 é adicionado o pacote org.springframework.data.jpa.convert.threeten no entityManagerFactoryBean.setPackagesToScan, dessa forma a aplicação irá encontrar os convertes do Spring.
Na hora da utilização tem apenas um detalhe que a necessidade do atributo columnDefinition
@NotNull @Column(name = "data_cadastro", columnDefinition = "DATE") private LocalDate dataCadastro = LocalDate.now();
- columnDefinition = “DATE”: o valor desse atributo é o tipo do banco de dados, no caso do exemplo feito em postgres, foi utilizado o tipo DATE da base de dados
Spring Boot / Spring Data
No Spring Data é fornecido esses convertes na classe Jsr310JpaConverters, com isso, caso esteja utilizando Spring Boot, apenas colocar a classe Jsr310JpaConverters para ser vista pela aplicação, através da entidade @EntityScan.
import org.springframework.data.jpa.convert.threeten.Jsr310JpaConverters; @EntityScan(basePackageClasses = {Entidade.class, Jsr310JpaConverters.class}) public class AppConfig {