/home/ericogr/blog

Experiências no desenvolvimento de aplicações e afins

Usando chaves compostas com JPA

Posted by ericogr em 15 abril 2008

Neste artigo vamos implementar uma entidade com uma chave primária composta de três campo. Tudo que for demonstrado aqui pode ser aplicado em uma aplicação real desktop ou web.

Com JPA, mapeamos tabelas e relacionamento de banco de dados através de classes chamadas entidades. Toda entidade deve ter uma chave primaria. Esta chave pode ter um ou mais campos que servem para identifica-la. Para chaves de um único campo, podemos especifica-la na própria entidade com a anotação @Id, mas em casos de mais de um, precisamos de classe que a represente, ou seja, para representar uma chave primária composta de mais de um campo, precisamos de uma classe extra. Os tipos aceitos para campos de chaves são os seguintes:

  • java.lang.String
  • java.util.Date
  • java.sql.Date
  • qualquer wrapper de tipo primitivo (int -> Integer, long -> Long, etc…) menos (não inteiros como float e double)

A classe que representará nossa primary key deve ser:

  • pública
  • conter um construtor sem argumentos
  • as propriedades (se você as mapeou com get/set) devem ter acesso público ou protegido
  • implementar Serializable
  • sobrescrever equals e hashcode adequadamente
  • deve ser anotada como @Embeddable
  • a classe que contiver a classe que representa a primary key deve ser anotada com @EmbaddableClass ou @EmbaddableId

Vamos trabalhar sobre um exemplo. Abaixo criei os passos para inserir e consultar dados com uma única entidade chamada Nota. Tentei simplificar ao máximo para podermos entender como tudo funciona. Esta consulta é feita utilizando um campo dentre três da chave primária. Utilizei este critério pois não é tão comum encontrar como fazer isso (queries com campos de chave primária composta), mas é bem simples. As tabelas no banco de dados são geradas automaticamente, graças ao parâmetro [toplink.ddl-generation= create-tables] em nosso persistence.xml.

1. Criar o arquivo de configuração persistence.xml

Para este exemplo, usamos o banco hsqldb. O arquivo abaixo armazena configurações para suas unidades de persistência. Você poderá adaptá-lo para seu banco de dados.

[persistence.xml]

<?xml version=”1.0″ encoding=”UTF-8″?>
<persistence version=”1.0″
xmlns=”http://java.sun.com/xml/ns/persistence”&gt;
<persistence-unit name=”testeJpaPU” transaction-type=”RESOURCE_LOCAL”>
<provider>
oracle.toplink.essentials.ejb.cmp3.EntityManagerFactoryProvider
</provider>

<!– entidades –>
<class>com.wordpress.entities.Nota</class>

<!– toplink/hsqldb –>
<properties>
<property name=”toplink.jdbc.url” value=”jdbc:hsqldb:hsql://localhost/testeJpa” />
<property name=”toplink.jdbc.user” value=”teste” />
<property name=”toplink.jdbc.driver” value=”org.hsqldb.jdbcDriver” />
<property name=”toplink.jdbc.password” value=”123456″ />
<property name=”toplink.target-database” value=”oracle.toplink.essentials.platform.database.HSQLPlatform”/>
<property name=”toplink.ddl-generation” value=”create-tables” />
<property name=”toplink.logging.level” value=”FINE” />
</properties>

</persistence-unit>
</persistence>

Vamos criar um pacote chamado com.wordpress.entities e também a classe entidade Nota:

[Nota.java]

package com.wordpress.entities;

import javax.persistence.EmbeddedId;
import javax.persistence.Entity;

@Entity
public class Nota {

private String descricao;
private NotaPk notaPk;

public Nota() {
}

@EmbeddedId
public NotaPk getNotaPk() {
return notaPk;
}

public void setNotaPk(NotaPk notaPk) {
this.notaPk = notaPk;
}

public String getDescricao() {
return descricao;
}

public void setDescricao(String descricao) {
this.descricao = descricao;
}

}

Agora a classe que representa a chave no banco de dados. Note que precisamos sobrescrever adequadamente os métodos equals e hashcode.

[NotaPk.java]

package com.wordpress.entities;

import java.io.Serializable;
import java.util.Date;

import javax.persistence.Embeddable;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;

@Embeddable
public class NotaPk implements Serializable {
private static final long serialVersionUID = 433046042456099827L;

private int numero1;
private int tipo;
private Date data;

public NotaPk() {
}

public NotaPk(int numero, int tipo, Date data) {
setNumero(numero);
setTipo(tipo);
setData(data);
}

public int getNumero() {
return numero1;
}

public void setNumero(int numero) {
this.numero1 = numero;
}

public int getTipo() {
return tipo;
}

public void setTipo(int tipo) {
this.tipo = tipo;
}

@Temporal(value = TemporalType.DATE)
public Date getData() {
return data;
}

public void setData(Date data) {
this.data = data;
}

@Override
public boolean equals(Object obj) {
if (obj == null || !(obj instanceof NotaPk)) {
return false;
}

NotaPk pk = (NotaPk)obj;

return (pk.getData().equals(this.getData()) &&
pk.getNumero() == this.getNumero() &&
pk.getTipo() == this.getTipo());
}

@Override
public int hashCode() {
int ret = 0;

ret =
getNumero() ^
getTipo() ^
(getData() != null ? getData().hashCode() : 0);

return ret;
}
}

Abaixo a classe principal que irá comprovar o funcionamento. Foi criada também uma query personalizada onde a busca é feita pelo campo número de nota. Veja como é feito o acesso aos campos chaves da entidade Nota.

[Main.java]

import java.util.Date;
import java.util.List;

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

import com.wordpress.entities.Nota;
import com.wordpress.entities.NotaPk;

public class Main {

public static void main(String[] args) {

new Main();
}

@SuppressWarnings(“unchecked”)
public Main() {
EntityManagerFactory emf = Persistence.createEntityManagerFactory(“testeJpaPU”);
EntityManager em = emf.createEntityManager();

Nota n = new Nota();
n.setNotaPk(new NotaPk(1, 2, new Date()));
n.setDescricao(“teste de nota”);

em.getTransaction().begin();

if (em.find(Nota.class, n.getNotaPk()) == null) {
em.persist(n);
}

Query q = em.createQuery(“select n from Nota n where n.notaPk.numero = 1“);
List<Nota> ns = (List<Nota>)q.getResultList();

for (Nota n1 : ns) {
System.out.println(“nota ” + n1.getNotaPk().getNumero());
}

em.getTransaction().commit();
em.close();
}

}

Todos os métodos das classes entidade e embeddable, que estiverem no padrão javabeans, serão associados aos campos do banco de dados. Somente os campos anotados com @Transient não serão persistidos.

Aqui utilizei Toplink e Hsqldb, mas podemos usar qualquer outro provedor e banco de dados. Basta especificar corretamente o arquivo persistence.xml

Bibliotecas utilizadas:

  • toplink-essentials.jar
  • toplink-essentials-agent.jar
  • hsqldb.jar

Estrutura de diretórios:

.
| hsqldb.jar
| toplink-essentials-agent.jar
| toplink-essentials.jar
|
\—src
| Main.java
|
+—com
| \—wordpress
| \—entities
| Nota.java
| NotaPk.java
|
\—META-INF
persistence.xml

Deixe uma resposta

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s

 
%d blogueiros gostam disto: