Hibernate com Java 11

Eduardo Ribeiro
8 min readJun 4, 2020

--

A configuração do Hibernate na versão 11 do Java difere um pouco quando comparada a versões anteriores (principalmente Java 8), devido à mudanças como a depreciação da API JAXB e a introdução do sistema Java Platform Module System (JPMS) no Java 9. Nesse artigo, irei demonstrar como implementar a persistência de dados com Hibernate no Java 11, além de fazer um breve tutorial para iniciantes sobre o uso desta ferramenta fundamental no desenvolvimento de aplicações Java.

Nota: Se você já conhece o Hibernate e está tendo problemas com a configuração no Java 11, no final do artigo eu apresento os erros que eu tive as soluções que encontrei.

Introdução ao Hibernate

Como você já deve saber, a linguagem de programação Java possui um paradigma orientada a objetos, o que significa que, de forma resumida, as classes criadas possuem seus próprios atributos e métodos acessíveis por qualquer instância da mesma. No entanto, o mesmo não pode ser dito para a linguagem SQL, utilizada nos bancos de dados relacionais. Na SQL, os dados são armazenados em linhas e colunas pertencentes a tabelas. Dadas as diferenças nas duas linguagens, como fazê-las comunicar-se entre si de modo que os dados de uma Aplicação Java sejam armazenados em um banco de dados?

Como resposta, surgiu o Framework Hibernate, uma ferramenta ORM (object-relational mapping) responsável pela persistência (armazenamento) de dados em Java.

Ferramentas ORM (Mapeamento Objeto-Relacional) são responsáveis por realizar o mapeamento de objetos, presentes em linguagens orientadas a objetos, com o paradigma relacional, presente em banco de dados relacionais.

Mas o que exatamente o Hibernate faz? Basicamente, ele é capaz de transformar os objetos instanciados em linhas de uma tabela, com colunas que apresentam os atributos declarados de cada objeto. Com isso, pode-se armazenar de forma simples os dados de uma Aplicação Java.

Persistência na prática

Suponha que queremos desenvolver uma aplicação de controle financeiro, que armazena seus gastos em categorias. Cada categoria possui um nome e uma lista com todos os gastos associados a ela, e cada gasto possui uma quantia e uma categoria ao qual ele pertence. Além destes atributos, ambas as classes terão ids próprios, que identificarão os objetos nas tabelas.

Nota: O projeto apresentado foi criado com a IDE IntelliJ IDEA e Maven, e está disponível na íntegra nesse repositório.

Em linguagem de banco de dados, dizemos que a categoria tem um relacionamento de um para muitos com os gastos, o que significa que uma categoria por ter múltiplos gastos. Analogamente, há uma relação de muitos pra um no relacionamento inverso, ou seja, existem vários gastos em uma única categoria.

Para começarmos a manipular o banco de dados, precisamos fazer duas coisas:

  • Incluir as dependências do Hibernate no arquivo pom.xml.
  • Atualizar o arquivo module-info.java com “requires java.persistence” para incluir a biblioteca de persistência do java. Isso se tornou necessário a partir do Java 9, quando foi incluído o sistema modular. Caso esse arquivo não exista, você pode cria-lo clicando com o botão direito na pasta Java, clique em Novo e selecione module-info.java.

Para Java 9 e versões posteriores: Adicione a linha “requires java.persistence” no módulo do projeto.

Feito isso, podemos começar a configurar a persistência dos dados propriamente dita. Para isso, incluímos a anotação Entity nas classes que queremos armazenadas no banco de dados, indicando que os objetos criados são entidades e devem ser persistidos. Em seguida, adicionamos a anotação Id no atributo id, indicando que essa é a chave primária da entidade. Por fim, precisamos fazer a ligação entre as classes categoria e gastos a partir dos relacionamentos de um para muitos e muitos para um descritos anteriormente. Isso é feito com as anotações OneToMany e ManyToOne, respectivamente.

Arquivo de configuração do Hibernate

O próximo passo é a criação do arquivo de configuração que definirá propriedades importantes do nosso banco de dados, como a escolha do Driver, o nome de usuário e a senha, o dialeto SQL, etc.

Nesse projeto, usaremos um banco de dados muito utilizado em testes de aplicações Java, o banco H2. Caso você opte por utilizar outros tipos disponíveis, como o MySQL, Apache Derby, etc, não se esqueça de modificar os parâmetros no arquivo de configuração.

Inclua a dependência do banco de dados escolhido. Para o H2, temos:

Em seguida, precisamos criar uma pasta chamada resources dentro da pasta main do projeto, e incluir o arquivo hibernate.cfg.xml dentro dela, com o código mostrado a seguir.

As propriedades do arquivo acima são:

  • hibernate.dialect: Determina o dialeto SQL usado pelo nosso banco de dados;
  • hibernate.show_sql: Determina se nós poderemos ver as queries em SQL ao longo do tempo de execução;
  • hibernate.connection.driver_class: O Driver escolhido para o banco de dados;
  • hibernate.connection.username e hibernate.connection.password: Nome de usuário e senha de acesso;
  • hibernate.connection.url: Caminho para o local onde arquivo binário .db será criado;

A árvore do projeto deve ficar semelhante a essa (a classe HibernateUtil será apresentada na próxima seção):

Nesse caso, será utilizado um banco de dados embarcado, o que significa que ele será armazenado na memória do computador na pasta definida pela propriedade “hibernate.connection.url”.

Criação da classe utilitária do Hibernate

Para começarmos a utilizar o Hibernate, precisamos de um método que crie uma Sessão. É por essa sessão que iremos acessar o banco de dados, para salvar, editar ou deletar (entre outras ações) as entidades de interesse. Para isso criaremos uma classe HibernateUtil.java, que será responsável por aplicar as configurações do Hibernate apresentadas no item anterior, bem como criar uma fábrica de sessões.

O código da classe HibernateUtil.java é uma “receita de bolo”, mas vale a pena explicar alguns trechos:

  • A fábrica de sessões é um objeto estático criado pelo método buildSessionFactory.
  • Nas primeiras linhas do método, é criado objeto Configuration que contém as propriedades do arquivo hibernate.cfg.xml.
  • Não se esqueça de incluir na configuração as classes das entidades a serem persistidas, através do método addAnnotatedClass.
  • A configuração criada é então ligada à fábrica de sessões, que é retornada no final do método.

Para Java 9 e versões posteriores: Adicione a linha “requires org.hibernate.orm.core” no módulo do projeto.

Main class

Agora que as configurações do Hibernate foram concluídas, podemos testar a aplicação. Na classe principal iremos criar dois objetos Categoria (moradia e alimentação) e criar 2 gastos para cada uma das categorias. Note que os objetos são instanciados com o id igual a null, o que é possível pois esse campo é preenchido automaticamente pelo Hibernate.

Na primeira linha da classe principal nós iniciamos uma sessão com o método openSession, chamado a partir da fábrica de sessões obtida pela classe utilitária HibernateUtil. Após a instanciação dos objetos Categoria e Gasto, nós usamos a sessão aberta para iniciar uma transação chamando o método beginTransaction, para então salvar os objetos com o método save.

Finalmente, concluímos fazendo um commit na transação e chamando o método shutdown para fechar a sessão do Hibernate.

Ações necessárias no Java 11

Se a versão do Java utilizada é a 8 (ou anteriores), a aplicação já pode ser executada sem problemas. Porém, se tentarmos executar os códigos em uma versão posterior (como Java 11) possivelmente obteremos vários erros em sequência, que não encontraríamos no java 8.

Abaixo vou apresentar os erros variados que obtive tentando rodar a aplicação e a solução para eles (é erro que não acaba mais!). Felizmente, a maioria deles pode ser corrigida escrevendo umas palavras mágicas no module-info.java.

# Erro 1

java: cannot access javax.naming.Referenceable /class file for javax.naming.Referenceable not found

  • Solução: Inclua “requires java.naming” no arquivo module-info.

# Erro 2

java: cannot access org.w3c.dom.Document / class file for org.w3c.dom.Document not found

  • Solução: Inclua “requires java.xml.bind” no arquivo module-info.

# Erro 3

Exception in thread “main” java.lang.ExceptionInInitializerError / Caused by: java.lang.NoClassDefFoundError: java/sql/SQLException

  • Solução: Inclua “requires java.sql” no arquivo module-info.

# Erro 4

Exception in thread “main” java.lang.ExceptionInInitializerError / Caused by: java.lang.NoClassDefFoundError:net/bytebuddy/NamingStrategy$SuffixingRandom$BaseNameResolver

  • Solução: Inclua “requires net.bytebuddy” no arquivo module-info.

# Erro 5

Exception in thread “main” java.lang.ExceptionInInitializerError / Caused by: javax.xml.bind.JAXBException: Implementation of JAXB-API has not been found on module path or classpath.

  • Solução: Inclua “requires com.sun.xml.bind” no arquivo module-info.

# Erro 6

Exception in thread “main” java.lang.ExceptionInInitializerError / Caused by: java.lang.NoClassDefFoundError: com/fasterxml/classmate/TypeResolver

  • Solução: Inclua uma nova dependência no pom.xml:

# Erro 7

Exception in thread “main” java.lang.ExceptionInInitializerError / Caused by: java.lang.reflect.InaccessibleObjectException: Unable to make field private java.lang.Integer medium.erc.Gasto.id accessible: module HibernateJava does not “opens medium.erc” to module org.hibernate.orm.core

Este erro ocorre porque o acesso das classes Categoria e Gasto não é aberto para o Hibernate. Mais detalhes aqui.

  • Solução: Inclua “opens medium.erc to org.hibernate.orm.core” no arquivo module-info.

Finalmente, após as correções de todos esses erros em sequência, a aplicação pode ser executada. O arquivo module-info corrigido deve ser semelhante a esse:

Perceba que foi criado um novo arquivo, database.mv.db, na pasta projeto. Ele nada mais é que o binário do banco de dados gerado com sucesso.

Bônus: Como acessar o banco de dados?

Os dados armazenados podem ser acessados por meio de um servidor. Para isso, baixe o arquivo .jar do banco de dados h2 aqui (clique em jar na linha “Files”).

Em seguida, abra um Terminal e escreva

java -cp {caminho para o jar} org.h2.tools.Server

No meu caso, usando Ubuntu e com terminal aberto na pasta home:

Executando o comando, será aberto no navegador uma página com a seguinte interface:

No campo JDBC URL, informe o local do banco de dados gerado na aplicação e clique em “Connect”:

jdbc:h2:{caminho para o projeto}/database

A partir da página gerada é possível acessar nosso banco de dados. Clique nas tabelas CATEGORIA e GASTO à esquerda ou escreva a query em SQL para retornar os objetos criados (não se esqueça de clicar em Run para executar).

Como pode ser visto, os objetos criados pela classe Main da aplicação foram armazenados no banco de dados com sucesso. Perceba a coluna CATEGORIA_ID, que demonstra o relacionamento de muitos para um entre o gasto e a categoria.

Como mencionado anteriormente, o projeto exemplo usado no artigo está disponível aqui.

--

--