Vamos criar relacionamentos com JPA? Como nos exemplos anteriores, focarei nos aspectos práticos fornecendo exemplos simplificados.
Os seguintes relacionamentos podem ser mapeados com JPA:
- OneToOne
- OneToMany
- ManyToOne
- ManyToMany
Também podemos usar genéricos para especificar os tipos de coleções persistidas. Elas podem ser:
- java.util.Collection
- java.util.Set
- java.util.List
- java.util.Map
Vamos ver um exemplo usando as classes Nota, ItemNota, Produto e Fornecedor. O objeto nota tem um ou mais objetos itemNota. O objeto itemNota tem uma instancia da classe Produto e cada instancia de produto tem uma ou mais instancias da classe Fornecedor. A entidade itemNota tem referencia para a classe Nota.
Este exemplo está configurado para apagar e criar as tabelas do banco de dados a cada nova execução, ou seja, cada vez que este exemplo for executado, as informações anteriores serão perdidas. Veja a configuração do arquivo persistence.xml
[persistence.xml]
</persistence>
<?xml version=”1.0″ encoding=”UTF-8″?>
<persistence version=”1.0″
xmlns=”http://java.sun.com/xml/ns/persistence”>
<persistence-unit name=”testeJpaPU” transaction-type=”RESOURCE_LOCAL”>
<provider>
oracle.toplink.essentials.ejb.cmp3.EntityManagerFactoryProvider
</provider><!– entidades –>
<class>com.wordpress.ericogr.entities.Nota</class>
<class>com.wordpress.ericogr.entities.ItemNota</class>
<class>com.wordpress.ericogr.entities.Produto</class>
<class>com.wordpress.ericogr.entities.Fornecedor</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=”drop-and-create-tables” />
<property name=”toplink.logging.level” value=”FINEST” />
</properties></persistence-unit>
Em alguns relacionamentos, usamos como parâmetro das anotações o código (cascade = CascadeType.ALL). Isto serve para que as alterações realizadas no objeto que a contém sejam propagadas para os dependentes. Para persistir a coleção de itens da nota, precisamos desta anotação, pois do contrário os itens não seriam persistidos (na prática seria disparada uma exceção).
A entidade Nota tem um relacionamento bidirecional com a entidade ItemNota, onde uma nota pode conter um ou mais itens e um item contém uma única nota.
Abaixo está a classe Nota que representa a nota fiscal. Ela tem uma descrição, uma coleção de itens de nota fiscal e uma chave composta, representada pela classe NotaPk. Note que a propriedade getItensNota() foi anotada com @OneToMany(cascade = CascadeType.ALL, mappedBy = “nota”) onde mappedBy indica o nome da propriedade (no caso getNota()) da entidade dona do relacionamento (ItemNota). Aqui poderíamos omitir, pois o nome da entidade Nota é identico ao nome da propriedade na classe ItemNota, porém, para deixar mais claro, decidi colocar.
[Nota.java]
package com.wordpress.ericogr.entities;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.OneToMany;
import javax.persistence.Table;@Entity
@Table(name = “nota”)
public class Nota implements Serializable {
private String descricao;
private NotaPk notaPk;
private Collection<ItemNota> itensNota = new ArrayList<ItemNota>();public Nota() {
}public Nota(NotaPk notaPk) {
setNotaPk(notaPk);
}@EmbeddedId
public NotaPk getNotaPk() {
return notaPk;
}public void setNotaPk(NotaPk notaPk) {
this.notaPk = notaPk;
}@Column(name = “descricao”, nullable = true)
public String getDescricao() {
return descricao;
}public void setDescricao(String descricao) {
this.descricao = descricao;
}@OneToMany(cascade = CascadeType.ALL, mappedBy = “nota”)
public Collection<ItemNota> getItensNota() {
return itensNota;
}public void setItensNota(Collection<ItemNota> itensNota) {
this.itensNota = itensNota;
}
}
Em seguida, tempos a classe NotaPk que representa uma chave composta para a entidade Nota. Precisamos desta classe extra, pois para identificar a entidade nota, temos 3 campos diferentes (número, tipo e data) diferente da classe Produto que tem somente um campo (id).
[NotaPk.java]
package com.wordpress.ericogr.entities;
import java.io.Serializable;
import java.util.Date;import javax.persistence.Column;
import javax.persistence.Embeddable;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;@Embeddable
public class NotaPk implements Serializable {
private static final long serialVersionUID = -6450890424150718733L;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);
}@Column(name = “numero”)
public int getNumero() {
return numero1;
}public void setNumero(int numero) {
this.numero1 = numero;
}@Column(name = “tipo”)
public int getTipo() {
return tipo;
}public void setTipo(int tipo) {
this.tipo = tipo;
}@Temporal(value = TemporalType.DATE)
@Column(name = “data”)
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;
}
}
Agora os itens da nota fiscal. Os itens são compostos por um id que o identifica unicamente, quantidade, produto e nota fiscal a que pertence.
[ItemNota.java]
package com.wordpress.ericogr.entities;
import java.io.Serializable;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.OneToOne;
import javax.persistence.Table;@Entity
@Table(name = “item_nota”)
public class ItemNota implements Serializable {
private int id;
private int quantidade;
private Produto produto;
private Nota nota;public ItemNota() {
}public ItemNota(int id, int quantidade, Produto produto) {
setId(id);
setQuantidade(quantidade);
setProduto(produto);
}@Id
public int getId() {
return id;
}public void setId(int id) {
this.id = id;
}@Column(name = “quantidade”)
public int getQuantidade() {
return quantidade;
}public void setQuantidade(int quantidade) {
this.quantidade = quantidade;
}@OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
public Produto getProduto() {
return produto;
}public void setProduto(Produto produto) {
this.produto = produto;
}@ManyToOne
public Nota getNota() {
return nota;
}public void setNota(Nota nota) {
this.nota = nota;
}
}
Produtos da nota fiscal: possui um id que o identifica, nome, peso, imagem (onde podemos gravar a figura do produto) e uma lista de fornecedores.
[Produto.java]
package com.wordpress.ericogr.entities;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.Table;@Entity
@Table(name = “produto”)
public class Produto implements Serializable {
private int id;
private String nome;
private float peso;
private byte[] imagem;
private List<Fornecedor> fornecedores = new ArrayList<Fornecedor>();public Produto() {
}public Produto(int id, String nome, float peso) {
setId(id);
setNome(nome);
setPeso(peso);
}@Id
@Column(name = “id”)
public int getId() {
return id;
}public void setId(int id) {
this.id = id;
}@Column(name = “nome”)
public String getNome() {
return nome;
}public void setNome(String nome) {
this.nome = nome;
}@Column(name = “peso”)
public float getPeso() {
return peso;
}public void setPeso(float peso) {
this.peso = peso;
}@Column(name = “imagem”)
public byte[] getImagem() {
return imagem;
}public void setImagem(byte[] imagem) {
this.imagem = imagem;
}@ManyToMany(cascade = CascadeType.ALL)
public List<Fornecedor> getFornecedores() {
return fornecedores;
}public void setFornecedores(List<Fornecedor> fornecedores) {
this.fornecedores = fornecedores;
}
}
O Cadastro de fornecedores é composto por um identificador (id), nome, endereço e produtos que fornece.
[Fornecedor.java]
package com.wordpress.ericogr.entities;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.Table;@Entity
@Table(name = “fornecedor”)
public class Fornecedor implements Serializable {
private int id;
private String nome;
private String endereco;
private List<Produto> produtos = new ArrayList<Produto>();public Fornecedor() {
}public Fornecedor(int id, String nome, String endereco) {
setId(id);
setNome(nome);
setEndereco(endereco);
}@Id
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 getEndereco() {
return endereco;
}public void setEndereco(String endereco) {
this.endereco = endereco;
}@ManyToMany(cascade = CascadeType.ALL)
public List<Produto> getProdutos() {
return produtos;
}public void setProdutos(List<Produto> produtos) {
this.produtos = produtos;
}
}
Finalmente a classe principal para testarmos o funcionamento das entidades que mapeamos. Criamos uma nota com itens, produtos e fornecedores e em seguida efetuamos uma consulta.
[Main.java]
package com.wordpress.ericogr;
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.ericogr.entities.Fornecedor;
import com.wordpress.ericogr.entities.ItemNota;
import com.wordpress.ericogr.entities.Nota;
import com.wordpress.ericogr.entities.NotaPk;
import com.wordpress.ericogr.entities.Produto;public class Main {
public static void main(String[] args) {
new Main();
}@SuppressWarnings(“unchecked”)
public Main() {
EntityManagerFactory emf;
EntityManager em;Nota nota1;
NotaPk notaPk1;
ItemNota itemNota1, itemNota2;
Produto produto1, produto2;
Fornecedor fornecedor1, fornecedor2;Query q;
emf = Persistence.createEntityManagerFactory(“testeJpaPU”);
em = emf.createEntityManager();//produto
produto1 = new Produto(1, “Cimento”, 5000);
produto2 = new Produto(2, “Piso 12×10″, 475.3f);//fornecedor
fornecedor1 = new Fornecedor(1, “JaxPCimentos”, “Rua Galeteri Blota, 123″);
fornecedor2 = new Fornecedor(2, “Kermit Ltda”, “Av Blogus, 987″);//fornecedor x produto
fornecedor1.getProdutos().add(produto1);
fornecedor2.getProdutos().add(produto1);
fornecedor2.getProdutos().add(produto2);//produto x fornecedor
produto1.getFornecedores().add(fornecedor1);
produto2.getFornecedores().add(fornecedor1);
produto2.getFornecedores().add(fornecedor2);//notaPk
notaPk1 = new NotaPk(1, 2, new Date());//item Nota
itemNota1 = new ItemNota(1, 120, produto1);
itemNota2 = new ItemNota(2, 151, produto2);//nota
nota1 = new Nota(notaPk1);
nota1.setDescricao(“teste de nota”);
nota1.getItensNota().add(itemNota1);
nota1.getItensNota().add(itemNota2);//item nota x nota
itemNota1.setNota(nota1);
itemNota2.setNota(nota1);em.getTransaction().begin();
em.persist(nota1);
em.getTransaction().commit();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]“);
System.out.println(“Nota nr: ” + n1.getNotaPk().getNumero() + “\n” +
“Nota tp: ” + n1.getNotaPk().getTipo() + “\n” +
“Nota dt: ” + n1.getNotaPk().getData() + “\n” +
“Nota ds: ” + n1.getDescricao() + “\n”);
for (ItemNota in : n1.getItensNota()) {
System.out.println(“[Itens]“);
System.out.println(“Item…: ” + in.getId() + “\n” +
“Prod…: ” + in.getProduto().getNome() + “\n” +
“Quant..: ” + in.getQuantidade() + “\n” +
“Ref Not: ” + in.getNota().getNotaPk().getNumero() + “\n”);
for (Fornecedor f : in.getProduto().getFornecedores()) {
System.out.println(“[Fornecedores]“);
System.out.println(“Fornecedor: ” + f.getId() + “\n” +
“Nome…..: ” + f.getNome() + “\n” );
}
}
}em.close();
}
}
Com este exemplo podemos observar como é fácil mapear os objetos para serem persistidos no banco de dados. Observamos o funcionamento das anotações @OneToOne, @OneToMany, @ManyToOne e @ManyToMany através de um exemplo simples que pode ser incrementado para um caso real.