set 05

Mapeando chaves compostas com JPA 2

Muitas vezes utilizamos em nossas tabelas chaves compostas para identificação de uma tupla, um exemplo seria na tabela empregado a chave primária ser composta pelos atributos cpf e matrícula do empregado, vocês podem dizer que bastaria o cpf ou a matrícula para identificar o empregado, mas aqui estou apenas exemplificando para mostrar como mapeamos esta chave composta utilizando JPA2.

Para isso a JPA nos oferece 2 estratégias de mapeamento:

  • IdClass
  • EmbeddedId

Eu até o momento ainda não identifiquei uma diferença de desempenho entre as duas estratégias, nem vejo vantagem em questão de verbosidade de uma em relação a outra, portanto mostrarei as 2 e quem quiser escolhe a que melhor convir.

Utilizando IdClass 

Para se utilizar de idClass precisamos criar uma classe com os atributos que compõem nossa chave primária da seguinte maneira:

public class EmpregadoId {

private String cpf;
private String matricula;

//getters e setters
@Override
public int hashCode() {
//código hashcode
}

@Override
public boolean equals(Object obj) {
//código equals
}
}

Devemos sempre implementar os métodos hashcode e equals da nossa classe de Id, o hibernate apenas dará um alerta caso não estejam implementados estes métodos.

Agora na nossa classe de entidade devemos ter o seguinte código:

@Entity
@IdClass(EmpregadoId.class)
public class Empregado {

@Id
private String cpf;

@Id
private String matricula;

//Outros atributos e getters e setters

Ou seja, basta anotar com @IdClass a classe de entidade, passando por parâmetro a classe que contém os atributos do id, e devemos anotar com @Id os atributos que compõem o id, que são os mesmos da classe de id.

E é só, nessa estratégia a classe que representa o id não precisa de anotações, e não será utilizada pela nossa aplicação, porém é necessária para que a estratégia funcione.

 

Utilizando EmbeddedId

Nesta  estratégia também precisamos de uma classe com os atributos da chave primária, porém esta classe deve ser anotada como @Embeddable e deve obrigatoriamente implementar a interface Serializable , além de implementar os métodos hashcode e equals assim como na estratégia anterior. Assim temos a classe de id da seguinte maneira:

@Embeddable
public class EmpregadoId implements Serializable {

private static final long serialVersionUID = 1L;
private String cpf;
private String matricula;

//getters e setters
@Override
public int hashCode() {
//código hashcode
}

@Override
public boolean equals(Object obj) {
//código equals
}
}

Já a nossa entidade ficará da seguinte maneira:

@Entity
public class Empregado {

@EmbeddedId
private EmpregadoId id;

//Outros atributos e getters e setters

Ou seja, agora para acessar os atributos da chave primária composta devemos buscar através do atributo id que será do tipo EmpregadoId, que é anotado com @EmbeddedId  .

_____________________________________________________________________________________

Vimos então como é simples mapear chaves primárias compostas utilizando JPA 2, o único problema é que sempre precisamos de uma classe auxiliar que possuirá os atributos que compõem a chave.

ago 01

Estados de um objeto gerenciado por um EntityManager

Neste post falarei um pouco de como a especificação JPA 2 trata as entidades manipuladas pelos EntityManagers. As principais funções dos EntityManagers são:

  1. Como o próprio nome já diz, gerenciar as entidades;
  2. Sincronizar o estado dos objetos com os dados correspondentes no banco de dados

Ao falarmos em gerenciar uma entidade nos referimos a como um EntityManager manipula uma entidade, e para isso é necessário entender os estados possíveis que uma instância pode assumir para um EntityManager. São 4 estados possíveis:

  • Transient

Neste estado seu objeto acabou de ser criado e o atributo que foi anotado com @Id não possui valor . Toda vez que você dá um new em um objeto de uma Entidade este é o estado que ele se encontrará.

  • Managed

Neste estado o objeto possui valor no atributo anotado com @Id, sendo este valor atribuido pelo EntityManager e não setado manualmente. Toda vez que ocorrer uma sincronização através de um flush ou um commit, os dados do objeto são atualizados no os dados do banco de dados.

  • Removed

Neste estado,assim como no estado managed, o objeto possui valor no atributo anotado com @Id, sendo este valor atribuido pelo EntityManager e não setado manualmente. Porém quando ocorrer uma sincronização através de um flush ou um commit, o objeto passa a não ter mais vínculo com o banco de dados.

  • Detached

Neste estado,assim como no estado managed, o objeto possui valor no atributo anotado com @Id, sendo este valor atribuido pelo EntityManager ou setado manualmente. Porém este objeto não possui mais vínculo com o EntityManager, logo ele não será mais sincronizado com o banco de dados, e qualquer alteração realizada com ele residirá somente na memória da aplicação e não no banco de dados.

Para trocar de estados o EntityManager nos fornece os seguinte métodos:

  • persist : Transient -> Managed
  • merge : Transient -> Managed / Detached -> Managed
  • evict, clear ou close : Managed -> Detached
  • remove : Managed -> Removed

 

Tendo esses estados em mente e também como transitar entre eles, você consegue ter um controle muito melhor sobre o que você está fazendo ao utilizar JPA.

Fica a dica!

 

 

nov 08

Configurando JPA com Hibernate

Neste post vou mostrar o básico para fazer funcionar o JPA  com hibernate , utilizando o hsqldb, que é um banco de dados leve e muito bom para testes de desenvolvimento.

Inicialmente precisaremos das bibliotecas do hibernate que nesta versão já vem com as do JPA 2, porém isso não influenciará muito nessa configuração básica caso esteja usando o JPA. Agora crie uma pasta lib para colocar os .jar e adicione-os ao build path do projeto.

O segundo passo é criar a nossa Persistence Unit, que provê a definição do contexto de persistência, contendo os metadados relativos a tal.


Para isso deve ser criado um arquivo XML chamado persistence.xml e deve ser colocado dentro da pasta META-INF dentro da sua pasta de códigos fontes, no eclipse, utilizando a configuração default ficaria :  “src/META-INF/persistence.xml” . Este arquivo ficará com a seguinte configuração :

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/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">
    <persistence-unit name="pu" transaction-type="RESOURCE_LOCAL">
        <provider>org.hibernate.ejb.HibernatePersistence</provider>
        <properties>
            <property name="hibernate.dialect" value="org.hibernate.dialect.HSQLDialect" />
            <property name="hibernate.connection.url" value="jdbc:hsqldb:hsql://localhost/nomedobanco" />
            <property name="hibernate.connection.driver_class" value="org.hsqldb.jdbcDriver" />

            <property name="hibernate.connection.password" value="senha" />
            <property name="hibernate.connection.username" value="usuario" />

            <property name="hibernate.archive.autodetection" value="class"/>
            <property name="hibernate.hbm2ddl.auto" value="update" />
            <property name="show_sql" value="true" />
            <property name="format_sql" value="true" />

        </properties>
    </persistence-unit>
</persistence>

Obs.: Caso esteja utilizando o JPA  troque a linha da tag <persistence> por:

<persistence version=”1.0″
xmlns=”http://java.sun.com/xml/ns/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_1_0.xsd”>

Feito isso precisamos agora configurar como pegar o EntityManager, que é uma interface para interagir com o contexto de persistência.

Para isso criaremos um singleton que através da fábrica EntityManagerFactory pegaremos uma nova instância de EntityManager caso não tenha nenhuma aberta, ou retornará a que já está aberta. Será criada a classe JPAUtil:

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

public class JPAUtil {
      private static EntityManagerFactory emf = null;
      private static EntityManager em = null;

      public static EntityManagerFactory getEntityManagerFactory() {
            if (emf == null)
                 emf = Persistence.createEntityManagerFactory("pu");
            return emf;
      }

       public static EntityManager getEntityManager() {
             if (em != null && em.isOpen())
                   return em;
             else {
                   em = getEntityManagerFactory().createEntityManager();
                   return em;
             }
       }

}

Isso já basta para testar se está funcionando, a conexão com o banco. Para testar agora criaremos uma classe de testes utilizando o JUnit para verificar se está tudo ok. Criaremos a classe JPATest:

import static org.junit.Assert.*;

import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;

import org.junit.Test;

public class JPAtest {
	@Test
	public void testaConexaoJPA() {
		EntityManager em = JPAUtil.getEntityManager();
		EntityTransaction transaction = em.getTransaction();
		transaction.begin();
		transaction.commit();
		boolean isconectado = em.isOpen();
		em.close();
		assertTrue(isconectado);
	}
}

Pronto, se tudo estiver correto ficará verde o teste do JUnit. Em outro post mostrarei como implementar um DAO para JPA.