JPA e Hibernate: Acessando dados em aplicações Java

Veja neste artigo como criar uma aplicação (CRUD) com Hibernate e JPA.

Trabalhar com manipulação de banco de dados tornou-se quase que obrigatório para qualquer sistema, do mais simples ao mais complexo. Até um tempo atrás tínhamos que usar técnicas e ferramentas para realizar tais tarefa, muito conhecidas como por exemplo o PreparedStatement, DriverManager e assim por diante.

Atualmente usamos frameworks ORM (Mapemanto Objeto-Relacional) que fazem toda a tarefa que antes era feita manualmente. Em poucas palavras um Framework ORM é responsável por abstrair um banco de dados relacional, permitindo que você trabalhe como se fosse um banco de dados orientado à objetos, o que é ideal para quem trabalha com uma linguagem que já é orientada à objetos, como é o caso do Java.

Neste artigo usaremos o Hibernate em conjunto com o JPA para criar um CRUD básico.

Diferença Hibernate e JPA

Muitos confundem a diferença entre o Hibernate e o JPA. O Hibernate é de fato o framework ORM, ou seja, a implementação física do que você usará para persistir, remover, atualizar ou buscar dados no SGBD. Por outro lado, o JPA é uma camada que descreve uma interface comum para frameworks ORM.

Você pode desenvolver todo seu sistema sem JPA, apenas com Hibernate ou qualquer outro framework ORM, como o TopLink. Porém você não pode desenvolver o sistema apenas com JPA, pois ele é apenas uma interface a ser utilizada por Frameworks ORM.

A ideia geral é tornar o sistema o mais abstrato possível e passível de mudanças sem grandes impactos. Se você desenvolver todo seu sistema usando JPA com o framework Hibernate e amanhã decide mudar para o TopLink, então as alterações serão mínimas.

Iniciando a construção do projeto

Crie um projeto Java normal, independente da IDE que você estiver usando. Precisamos, antes de iniciar, adicionar as dependências/bibliotecas necessárias para funcionamento do nosso projeto, são elas:

Na opção código fonte, no topo desse post encontrará todas essas bibliotecas para downloads.

Configurando o persistence.xml

Feita a adição das bibliotecas no classhpath do seu projeto, você precisará de um arquivo chamado persistence.xml que é usado pelo JPA para realizar as configurações importantes para o projeto, tais como: conexão com o banco de dados e outras configurações extras que veremos na Listagem 1.

<?xml version="1.0" encoding="UTF-8" ?> <persistence xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd" version="2.0" xmlns="http://java.sun.com/xml/ns/persistence"> <persistence-unit name="crudHibernatePU" transaction-type="RESOURCE_LOCAL"> <provider>org.hibernate.ejb.HibernatePersistence</provider> <properties> <property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect" /> <property name="javax.persistence. jdbc.driver" value="org.postgresql.Driver" /> <property name="javax.persistence. jdbc.url" value="jdbc:postgresql://localhost:5432/teste" /> <property name="javax.persistence. jdbc.user" value="postgres" /> <property name="javax.persistence. jdbc.password" value="pgadmin" /> <property name="hibernate. show_sql" value="true" /> <property name="hibernate. format_sql" value="false" /> <property name="hibernate. use_sql_comments" value="false" /> <property name="hibernate. jdbc.wrap_result_sets" value="false" /> <property name="hibernate. hibernate.cache.use_query_cache" value="true" /> <property name="hibernate. hbm2ddl.auto" value="update" /> </properties> </persistence-unit> </persistence>
Listagem 1. persistence.xml

Na tag devemos definir qual o nome da nossa unidade de persistência, através da propriedade name que em nosso caso é crudHibernatePU. Esse nome será usado programaticamente para que o JPA saiba quais são as configurações que ele deve usar, como um binding entre a aplicação e o arquivo persistence.xml.

A tag provider identifica qual o framework que estamos utilizando em conjunto com o JPA, no nosso caso é o Hibernate. Abaixo temos várias tags , a hibernate.dialect define um dialeto padrão para escrita de comandos SQL e o jdbc.driver define qual o driver do banco que estamos utilizando, em nosso caso o org.postgresql.Driver.

Nas três últimas propriedades temos o link de conexão ao banco, o usuário e finalmente a senha. Para quem já trabalhou com o DriverManager vai notar uma pequena semelhança com a conexão JDBC que fazíamos lá.

<property name="hibernate.hbm2ddl.auto" value="update" />

Esta propriedade apresentada configura o que o Hibernate deve fazer com o banco de dados ao ser iniciado. No caso acima estamos definindo o seu valor como “update”, isso significa que toda vez ao iniciar a nossa aplicação o Hibernate irá checar por alterações entre o mapeamento e o banco de dados, como uma propriedade adicionada em uma classe. Quando é encontrada alguma alteração então o Hibernate executa o DDL para atualizar o banco de dados.

É muito importante ter cuidado quando esta propriedade estiver sendo usada pois ela pode alterar o banco de dados de uma forma que você não desejava, por isso tenha certeza de que realmente precisa dela.

Você deve criar um diretório chamado META-INF e colocar dentro do diretório src. Dentro do META-INF você deve colocar o persistence.xml.

DAO para comunicação com o banco de dados

O próximo passo é criar uma classe DAO (Data Access Object) que fará a comunicação com o banco de dados, realizando as operações básicas (CRUD). Toda a chamada para da aplicação será feita para nosso DAO que se responsabilizará pela comunicação com o banco de dados.

Nosso DAO irá fazer CRUD com uma classe chamada Cliente, para isso vejamos como mapear nossa classe Cliente com a Listagem 2.

package br.com.crudhibernate; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; @Entity @Table(name = "cliente") public class Cliente { @Id private int id; @Column private String nome; @Column private String cpf; @Column private String rg; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getNome() { return nome; } public void setNome(String nome) { this.nome = nome; } public String getCpf() { return cpf; } public void setCpf(String cpf) { this.cpf = cpf; } public String getRg() { return rg; } public void setRg(String rg) { this.rg = rg; } }
Listagem 2. Classe Cliente, JPA Annotation

Além da criação padrão da classe Cliente que possui os atributos id, nome, CPF e RG com os seus respectivos getters e setters, temos também algumas anotações definidas pelo JPA:

@Entity @Table(name = "cliente") public class Cliente {

A anotação @Entity faz com que o JPA saiba que aquela classe deve ser manipulada por ele, e a anotação @Table define que esta classe possui uma tabela chamada cliente no banco de dados.

O primeiro atributo “id” é uma chave primaria e precisamos obrigatoriamente definir a anotação @Id para que o JPA não apresente erros ao tentar inicializar a aplicação, as próximas propriedades possuem a anotação @Column que descrevem que estas propriedades possuem uma coluna correspondente no banco de dados.

Você nem precisa ter criado a tabela cliente com estas colunas, apenas precisa ter o banco de dados em execução. Quando a aplicação for inicializada o próprio JPA irá se responsabilizar por criar essa tabela com suas colunas caso ela não exista, isso porque estamos usando o valor “update” para a propriedade hibernate.hbm2ddl.auto.

Definida a nossa classe Cliente, podemos construir a classe DAO responsável por fazer a comunicação com o banco de dados, como mostra a Listagem 3.

package br.com.crudhibernate; import java.util.List; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.Persistence; public class ClienteJpaDAO { private static ClienteJpaDAO instance; protected EntityManager entityManager; public static ClienteJpaDAO getInstance(){ if (instance == null){ instance = new ClienteJpaDAO(); } return instance; } private ClienteJpaDAO() { entityManager = getEntityManager(); } private EntityManager getEntityManager() { EntityManagerFactory factory = Persistence.createEntityManagerFactory("crudHibernatePU"); if (entityManager == null) { entityManager = factory.createEntityManager(); } return entityManager; } public Cliente getById(final int id) { return entityManager.find(Cliente.class, id); } @SuppressWarnings("unchecked") public List<Cliente> findAll() { return entityManager.createQuery("FROM " + Cliente.class.getName()).getResultList(); } public void persist(Cliente cliente) { try { entityManager.getTransaction().begin(); entityManager.persist(cliente); entityManager.getTransaction().commit(); } catch (Exception ex) { ex.printStackTrace(); entityManager.getTransaction().rollback(); } } public void merge(Cliente cliente) { try { entityManager.getTransaction().begin(); entityManager.merge(cliente); entityManager.getTransaction().commit(); } catch (Exception ex) { ex.printStackTrace(); entityManager.getTransaction().rollback(); } } public void remove(Cliente cliente) { try { entityManager.getTransaction().begin(); cliente = entityManager.find(Cliente.class, cliente.getId()); entityManager.remove(cliente); entityManager.getTransaction().commit(); } catch (Exception ex) { ex.printStackTrace(); entityManager.getTransaction().rollback(); } } public void removeById(final int id) { try { Cliente cliente = getById(id); remove(cliente); } catch (Exception ex) { ex.printStackTrace(); } } }
Listagem 3. Classe DAO

Nossa classe ClienteJpaDAO segue o padrão de projeto Singleton que garante que apenas uma instância dessa classe será criada durante toda a aplicação. Ao realizar a criação da classe pela primeira vez o método getEntityManager() é chamado, responsável por criar uma instância de EntityManager.

A linha Persistence.createEntityManagerFactory(crudHibernatePU) usa as configurações presentes no arquivo persistence.xml para criar uma instância de EntityManagerFactory. Depois disso verificamos se o atributo entityManager é nulo, ou seja, nunca foi criado, sendo assim usamos o createEntityManager() para criar uma instância de EntityManager que é responsável por realizar as operações de CRUD no banco de dados.


Saiba mais sobre CRUD no banco de dados ;)

Tudo gira em torno do EntityManager, este é o nosso objeto principal para o CRUD. Feito isso e entendido para que precisamos dele, podemos começar a criar os métodos que usarão tão atributo. Vejamos o getById():

public Cliente getById(final int id) { return entityManager.find(Cliente.class, id); }

Perceba a simplicidade apresentada no método acima, o simples find() chamado do EntityManager passando o tipo da classe o id que deve ser buscado, já traz um objeto Cliente. Se fossemos usar a técnica padrão de busca com JDBC, deveríamos criar um PreparedStatement que retorna um ResultSet, depois pegar o primeiro registro do ResultSet para só então hidratar um objeto Cliente com os dados do ResultSet, enfim o trabalho seria muito maior, sem dúvida.

O próximo método é o finAll():

public List<Cliente> findAll() { return entityManager.createQuery("FROM " + Cliente.class.getName()).getResultList(); }

Este utiliza o createQuery que recebe um JPQL (Java Persistence Query Language) que é uma alternativa ao SQL em forma de orientação a objetos, ou seja, você pode realizar as consultas como se o banco de dados fosse orientado a objetos.

O método persist() inicia uma transação através do getTransaction(). begin() e finaliza a transação no commit(), dentro dessa transação é executado o método persist() que salva o objeto cliente. Caso algum erro ocorra então um rollback() é executado e todas as alterações são desfeitas.

O método merge segue o mesmo princípio do método persist(), a única diferença é que o merge atualiza o registro e não apenas insere ele no banco. Mas se o registro não existir no banco e você usar o método merge() ele irá inserir o mesmo.

Seguindo o mesmo princípio dos outros o método remove() primeiro procura o cliente no banco de dados para só então remover. O removebyId() recebe direto o id que deve ser removido enquanto que o remove() recebe o objeto.

Vamos criar um formulário para realizar as operações de inserção, deleção, atualização e busca do Cliente, como mostra a Figura 1.

Figura 1. Formulário CRUD Cliente

Nosso formulário possui quatro campos básicos, aqueles definidos na nossa classe Cliente e três botões para ações distintas.

Vejamos as ações dos três botões, cada um em um método distinto, um ActionListener, como mostra a Listagem 4.

private void jButtonSalvarActionPerformed(java.awt.event.ActionEvent evt) { Cliente cliente = new Cliente(); cliente.setCpf(jTextFieldCpf.getText()); cliente.setId(Integer.parseInt(jTextFieldID.getText())); cliente.setNome(jTextFieldNome.getText()); cliente.setRg(jTextFieldRg.getText()); ClienteJpaDAO.getInstance().merge(cliente); clearFields(); JOptionPane.showMessageDialog(this, "Salvo com sucesso !"); } private void jButtonRemoverActionPerformed(java.awt.event.ActionEvent evt) { ClienteJpaDAO.getInstance().removeById(Integer.parseInt(jTextFieldID.getText())); clearFields(); JOptionPane.showMessageDialog(this, "Removido com sucesso !"); } private void jButtonBuscarActionPerformed(java.awt.event.ActionEvent evt) { int id = Integer.parseInt(JOptionPane.showInputDialog("Digite o id do cliente")); Cliente cliente = ClienteJpaDAO.getInstance().getById(id); jTextFieldCpf.setText(cliente.getCpf()); jTextFieldID.setText(String.valueOf(cliente.getId())); jTextFieldNome.setText(cliente.getNome()); jTextFieldRg.setText(cliente.getRg()); } private void clearFields(){ jTextFieldCpf.setText(""); jTextFieldID.setText(""); jTextFieldNome.setText(""); jTextFieldRg.setText(""); }
Listagem 4. ActionListeners dos botões

O primeiro método jButtonSalvarActionPerformed() cria um objeto cliente hidratando-o com os dados preenchidos no JtextFIelds do formulário, posteriormente chamamos o método merge() do ClienteJpaDAO que é responsável por inserir ou atualizar o registro. Posteriormente chamamos o método clearFields() que limpa os campos para que o usuário possa preencher com as informações de um outro cliente.

Em seguida temos o método jButtonRemoverActionPerformed() que apenas pega o ID digitado no JtextFieldID e passa para o método removeById() do ClienteJpaDAO É importante salientar que em nenhum momento estamos passando comandos SQL, HQL ou JPQL, e poderíamos melhorar ainda mais o nível de acoplamento do nosso DAO permitindo que o CRUD seja realizado com qualquer tipo de objeto e não apenas Cliente.

Depois temos o método jButtonBuscarActionPerformed() que pergunta ao usuário qual o ID do cliente e usar o método getById() para retornar o objeto Cliente hidratado. O método clearFields() serve apenas para limpar os campos jTextFields.

Se você desejar usar o netBeans para construir o formulário, poderá usar o código “.form” da Listagem 5 que construirá o mesmo layout mostrado na Figura 1.

<?xml version="1.0" encoding="UTF-8" ?> <Form version="1.3" maxVersion="1.9" type="org.netbeans .modules.form.forminfo.JFrameFormInfo"> <Properties> <Property name="defaultCloseOperation" type="int" value="3"/> </Properties> <SyntheticProperties> <SyntheticProperty name="formSizePolicy" type="int" value="1"/> <SyntheticProperty name="generateCenter" type="boolean" value="false"/> </SyntheticProperties> <AuxValues> <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="0"/> <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/> <AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/> <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/> <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/> <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/> <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/> <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/> <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/> </AuxValues> <Layout> <DimensionLayout dim="0"> <Group type="103" groupAlignment="0" attributes="0"> <Group type="102" attributes="0"> <EmptySpace max="-2" attributes="0"/> <Group type="103" groupAlignment="0" attributes="0"> <Group type="102" attributes="0"> <Group type="103" groupAlignment="0" attributes="0"> <Component id="jLabel1" alignment="0" min="-2" max="-2" attributes="0"/> <Component id="jTextFieldID" alignment="0" min="-2" pref="242" max="-2" attributes="0"/> </Group> <EmptySpace type="separate" max="-2" attributes="0"/> <Group type="103" groupAlignment="0" attributes="0"> <Component id="jLabel2" alignment="0" min="-2" max="-2" attributes="0"/> <Component id="jTextFieldNome" alignment="0" min="-2" pref="242" max="-2" attributes="0"/> </Group> <EmptySpace min="0" pref="0" max="32767" attributes="0"/> </Group> <Group type="102" attributes="0"> <Group type="103" groupAlignment="0" attributes="0"> <Component id="jLabel3" alignment="0" min="-2" max="-2" attributes="0"/> <Component id="jTextFieldCpf" alignment="0" min="-2" pref="242" max="-2" attributes="0"/> </Group> <EmptySpace max="32767" attributes="0"/> <Group type="103" groupAlignment="0" attributes="0"> <Component id="jLabel4" alignment="0" min="-2" max="-2" attributes="0"/> <Component id="jTextFieldRg" alignment="0" min="-2" pref="242" max="-2" attributes="0"/> </Group> </Group> </Group> <EmptySpace max="32767" attributes="0"/> </Group> <Group type="102" alignment="0" attributes="0"> <EmptySpace min="-2" pref="26" max="-2" attributes="0"/> <Component id="jButtonSalvar" min="-2" max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/> <Component id="jButtonRemover" min="-2" max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/> <Component id="jButtonBuscar" min="-2" max="-2" attributes="0"/> <EmptySpace max="32767" attributes="0"/> </Group> </Group> </DimensionLayout> <DimensionLayout dim="1"> <Group type="103" groupAlignment="0" attributes="0"> <Group type="102" alignment="0" attributes="0"> <EmptySpace max="-2" attributes="0"/> <Group type="103" groupAlignment="1" attributes="0"> <Group type="102" attributes="0"> <Component id="jLabel1" min="-2" max="-2" attributes="0"/> <EmptySpace type="unrelated" max="-2" attributes="0"/> <Component id="jTextFieldID" min="-2" max="-2" attributes="0"/> </Group> <Group type="102" attributes="0"> <Component id="jLabel2" min="-2" max="-2" attributes="0"/> <EmptySpace type="unrelated" max="-2" attributes="0"/> <Component id="jTextFieldNome" min="-2" max="-2" attributes="0"/> </Group> </Group> <EmptySpace type="separate" max="-2" attributes="0"/> <Group type="103" groupAlignment="1" attributes="0"> <Group type="102" attributes="0"> <Component id="jLabel3" min="-2" max="-2" attributes="0"/> <EmptySpace type="unrelated" max="-2" attributes="0"/> <Component id="jTextFieldCpf" min="-2" max="-2" attributes="0"/> </Group> <Group type="102" attributes="0"> <Component id="jLabel4" min="-2" max="-2" attributes="0"/> <EmptySpace type="unrelated" max="-2" attributes="0"/> <Component id="jTextFieldRg" min="-2" max="-2" attributes="0"/> </Group> </Group> <EmptySpace type="separate" max="-2" attributes="0"/> <Group type="103" groupAlignment="3" attributes="0"> <Component id="jButtonBuscar" alignment="3" min="-2" max="-2" attributes="0"/> <Component id="jButtonRemover" alignment="3" min="-2" max="-2" attributes="0"/> <Component id="jButtonSalvar" alignment="3" min="-2" max="-2" attributes="0"/> </Group> <EmptySpace pref="25" max="32767" attributes="0"/> </Group> </Group> </DimensionLayout> </Layout> <SubComponents> <Component class="javax.swing.JLabel" name="jLabel1"> <Properties> <Property name="text" type="java.lang.String" value="ID"/> </Properties> </Component> <Component class="javax.swing.JTextField" name="jTextFieldID"> <Properties> <Property name="name" type="java.lang.String" value="jTextFieldId" noResource="true"/> </Properties> <Events> <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="jTextFieldIDActionPerformed"/> </Events> </Component> <Component class="javax.swing.JLabel" name="jLabel2"> <Properties> <Property name="text" type="java.lang.String" value="Nome"/> </Properties> </Component> <Component class="javax.swing.JTextField" name="jTextFieldNome"> <Properties> <Property name="name" type="java.lang.String" value="jTextFieldNome" noResource="true"/> </Properties> </Component> <Component class="javax.swing.JLabel" name="jLabel3"> <Properties> <Property name="text" type="java.lang.String" value="CPF"/> </Properties> </Component> <Component class="javax.swing.JTextField" name="jTextFieldCpf"> <Properties> <Property name="name" type="java.lang.String" value="jTextFieldCpf" noResource="true"/> </Properties> </Component> <Component class="javax.swing.JTextField" name="jTextFieldRg"> <Properties> <Property name="name" type="java.lang.String" value="jTextFieldRg" noResource="true"/> </Properties> </Component> <Component class="javax.swing.JLabel" name="jLabel4"> <Properties> <Property name="text" type="java.lang.String" value="RG"/> </Properties> </Component> <Component class="javax.swing.JButton" name="jButtonSalvar"> <Properties> <Property name="text" type="java.lang.String" value="Salvar"/> <Property name="name" type="java.lang.String" value="jButtonSalvar" noResource="true"/> </Properties> <Events> <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="jButtonSalvarActionPerformed"/> </Events> </Component> <Component class="javax.swing.JButton" name="jButtonBuscar"> <Properties> <Property name="text" type="java.lang.String" value="Buscar"/> <Property name="name" type="java.lang.String" value="jButtonBuscar" noResource="true"/> </Properties> <Events> <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="jButtonBuscarActionPerformed"/> </Events> </Component> <Component class="javax.swing.JButton" name="jButtonRemover"> <Properties> <Property name="text" type="java.lang.String" value="Remover"/> <Property name="name" type="java.lang.String" value="jButtonRemover" noResource="true"/> </Properties> <Events> <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="jButtonRemoverActionPerformed"/> </Events> </Component> </SubComponents> </Form>
Listagem 5. Código do arquivo .form

Neste artigo vimos como criar um CRUD usando o Hibernate e o JPA. Explicamos o passo a passo desde a criação do projeto até a inserção, atualização, deleção ou busca de registros no banco de dados.

A versão do Hibernate usado para este artigo foi a 4.2.19 final e o JPA 2.0, todas disponíveis na opção “código fonte” deste artigo.


Links Úteis


Saiba mais sobre RMAN ;)

Artigos relacionados