Para efetuar o download você precisa estar logado. Clique aqui para efetuar o login

Hibernate: Persistindo datas no Java sem incompatibilidade de tipos

Veja neste artigo como utilizar a API de datas do Java 8 com o Hibernate sem problemas de mapeamento.

Você precisa estar logado para dar um feedback. Clique aqui para efetuar o login
Para efetuar o download você precisa estar logado. Clique aqui para efetuar o login
Confirmar voto
0
 (7)  (0)

Motivação

Com o lançamento da API Date and Time, cujo objetivo principal é simplificar a manipulação de datas, um pequeno problema surgiu: os novos tipos de dados não eram completamente suportados pela especificação da JPA 2.1. Dessa forma, classes como LocalDate podem ser utilizadas como atributo de uma entidade, mas devido à incompatibilidade, não é possível anotar esses atributos com @Temporal e armazená-los no banco de dados.

Como o lançamento do JPA 2.2 ainda não ocorreu (até o momento da publicação desse artigo), uma das implementações da JPA criou uma solução própria. O Hibernate 5 tornou possível o mapeamento de classes da API de datas do Java 8 com os tipos JDBC. A Figura 1 mostra o mapeamento criado entre os novos tipos do pacote java.time e os tipos JDBC.

Mapeamento de tipos da java.time e do JDBC
Figura 1. Mapeamento de tipos da java.time e do JDBC

Nesse artigo, veremos como utilizar a API de datas do Java 8 juntamente com o Hibernate 5.

Passo 1: Criar o projeto

Para o exemplo que desenvolveremos aqui, vamos utilizar o MySQL 5.7.11, o NetBeans 8.2 e, com eles, criaremos uma aplicação exemplo simples, contendo apenas uma entidade, chamada Ordem, cujo script SQL para criação no banco de dados é apresentado na Listagem 1.

CREATE TABLE ordem (
  01 ordemId bigint(20) NOT NULL AUTO_INCREMENT,
  02 OrdemProduct varchar(255) DEFAULT NULL,
  03 ordemCreated timestamp NULL DEFAULT NULL,
  04 ordemDate date DEFAULT NULL,
  05 ordemTime time DEFAULT NULL,
  06 ordemStart timestamp,
  07 ordemEnd timestamp,
  08 ordemTimeExecuted bigint(20), 
  09 ordemTimeZonedTime timestamp,
  10 PRIMARY KEY (ordemId) );
Listagem 1. Script para criação da tabela Ordem

Após criar o banco de dados, crie um novo projeto Java, chamado hibernate-time, e adicione as bibliotecas referentes ao Hibernate Core em seu classpath. A versão do Hibernate que faremos uso é a 5.2.6.Final, a mais recente no momento da escrita desse material.

Passo 2: Mapear as classes do domínio da aplicação

Para definir o mapeamento objeto/relacional da entidade do domínio da aplicação, adicione ao projeto uma nova classe chamada Ordem, cujo código pode ser visto na Listagem 2.

  01 //imports omitidos   
  02
  03 @Entity
  04 @Table(name = "ordem")
  05 public class Ordem {
  06     
  07    @Id
  08    @GeneratedValue(strategy = GenerationType.IDENTITY)
  09    private Long ordemId;
  10
  11    private String OrdemProduct;
  12    private LocalDate ordemDate;
  13    private LocalTime ordemTime;
  14    private LocalDateTime ordemCreated;
  15    private ZonedDateTime ordemTimeZonedTime;
  16    private Instant ordemStart;
  17    private Instant ordemEnd;
  18    private Duration ordemTimeExecuted;
  19
  20   //construtores e métodos gets e sets omitidos  
  21 } 
  
Listagem 2. Código da classe Ordem

Nesse exemplo, fizemos uso dos novos tipos da API Java Time para definir os atributos da classe. LocalTime, LocalDate e LocalDateTime, provavelmente, são as classes mais utilizadas ao lidar com essa API, e representam, respectivamente, uma data, um horário e os dois juntos. A classe ZonedDateTime representa uma data e hora em um fuso horário específico. Já a classe Instant, diz respeito a um ponto no tempo com a precisão de nanosegundos. A classe Duration, por sua vez, geralmente utiliza internamente a classe anterior e serve para medir um intervalo de tempo em nanosegundos.

As anotações presentes nessa classe são: @Entity e @Table, que indicam que os objetos dessa classe serão persistidos no banco de dados; além de @GeneratedValue e @Id, para criação de uma chave primária para o objeto.

O destaque aqui fica para a ausência da anotação @Temporal nos demais atributos. O Hibernate 5 tem informações suficientes para inferir o mapeamento correto da propriedade dentro do banco. Com isso, ele lê, por exemplo, o tipo LocalDateTime e, corretamente, insere o dado no banco de dados como um timestamp.

Passo 3: Configurar o acesso ao banco de dados

Agora, é necessário informar ao Hibernate onde está o banco de dados e as informações necessárias para se conectar a ele. Portanto, crie um arquivo XML de nome hibernate.cfg, na pasta src do projeto, e defina nele as configurações comumente utilizadas.

O passo seguinte é codificar a classe utilitária HibernateUtil, que fará a ligação entre o arquivo XML e o banco de dados, disponibilizando uma instância de SessionFactory para obter um objeto de sessão. A Listagem 3 mostra a implementação dessa classe.

  01 //imports omitidos
  02
  03 public class HibernateUtil {
  04
  05  private static SessionFactory sessionFactory;
  06
  07  public static SessionFactory getSessionFactory() {
  08    if (sessionFactory == null) {         
  09       Configuration configuration = new Configuration().configure();           
  10       sessionFactory = configuration.buildSessionFactory();
  11     }
  12    return sessionFactory;
  13   }
  14  }
  
Listagem 3. Código da classe HibernateUtil.

Passo 4: Criar a classe DAO

Com a classe de persistência mapeada com as anotações do Hibernate, o arquivo de configuração e a conexão com o banco de dados implementados, partiremos para a criação do DAO, OrdemDAO, responsável por disponibilizar as operações de persistência. Seu código pode ser visto na Listagem 4.

  01 //imports omitidos...
  02
  03 public class OrdemDAO { 
  04
  05     private static SessionFactory factory; 
  06
  07     public OrdemDAO() { 
  08           factory = HibernateUtil.getSessionFactory();  
  09     } 
  10
  11     public void addOrdem(Ordem ordem) { 
  12           Session session = factory.openSession(); 
  13           Transaction tx = null;
  14           Integer cod_aluno = null; 
  15
  16           try { 
  17                  tx = session.beginTransaction(); 
  18                  session.save(ordem); 
  19                  tx.commit();
  20           } 
  21           catch (HibernateException e) { 
  22                  if (tx != null) {
  23                         tx.rollback(); 
  24                  } 
  25                  e.printStackTrace(); 
  26           } finally { 
  27                  session.close(); 
  28           } 
  29     }
  
Listagem 4. Código da classe OrdemDAO.

Linhas 7 a 9: Obtemos uma instância da sessão a partir da classe utilitária HibernateUtil;

Linhas 11 a 29: Definimos o método de inserção de um objeto do tipo Ordem no banco de dados.

Passo 5: Testar a aplicação

Por fim, chega o momento de testar a aplicação. A execução da classe Main, apresentada na Listagem 5, permite visualizar as inserções no banco de dados.

Na linha 5 é criado um objeto do tipo Ordem e em seguida seus atributos são preenchidos. Esse objeto é persistido no banco de dados através da chamada ao método addOrdem(), da classe OrdemDAO.

  01 //imports omitidos…
  02
  03 public class Main {
  04    public static void main(String[] args) {       
  05            Instant inicio = Instant.now();
  06            Ordem ordem1=new Ordem();
  07            ordem1.setOrderProduct("Internet Link");
  08            ordem1.setOrderDate(LocalDate.now());
  09            ordem1.setOrdemTime(LocalTime.now());
  10            
  11            ZoneId fusoHorario = ZoneId.of("America/Sao_Paulo");
  12            ZonedDateTime agoraEmSaoPaulo = ZonedDateTime.now(fusoHorario);
  13            ordem1.setOrdemTimeZonedTime(agoraEmSaoPaulo);
  14            ordem1.setOrderCreated(LocalDateTime.now());  
  15            ordem1.setOrdemStart(inicio);
  16            Instant fim = Instant.now();
  17            ordem1.setOrdemStart(fim);
  18            ordem1.setOrdemTimeExecuted(Duration.between(inicio, fim));
  19            OrdemDAO ord = new OrdemDAO();
  20            ord.addOrdem(ordem1);
  21     }
  21 }
  
Listagem 5. Implementação da classe de teste, Main.

A Figura 2 mostra a visão do banco de dados após a execução da classe de teste. Veja que os valores definidos foram inseridos corretamente no banco de dados.

Banco de dados após a execução da classe Main
Figura 2. Banco de dados após a execução da classe Main

O Hibernate 5, como vimos, simplificou o trabalho com as classes da API de datas do Java 8, evitando a necessidade de efetuar várias conversões a fim de manter a compatibilidade entre os tipos de dados dos objetos e do banco.

Carregando
 
Você precisa estar logado para dar um feedback. Clique aqui para efetuar o login
Ajude-nos a evoluir: você gostou do post?  (7)  (0)
Confirmar voto
Compartilhe:
Ficou com alguma dúvida?