/home/ericogr/blog

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

Posts de Abril, 2008

Usando Callback Listeners com JPA

Publicado por ericogr em 28 Abril 2008

Podemos definir “callback listeners” como classes cujos métodos, devidamente anotados, são chamados em resposta a eventos ocorridos dentro do ciclo de vida de eventos de uma entidade. Exemplo: temos uma entidade Nota e queremos criar um log para todas as operações em que ela é adicionada, atualizada ou removida. Também podemos comparar com “triggers” em banco de dados.

As seguintes operações são suportadas:

  • Pré-persistência (@PrePersist)
  • Pré-atualização (@PreUpdate)
  • Pré-remoção (@PreRemove)
  • Pós-persistencia (@PostPersist)
  • Pós-atualização (@PostUpdate)
  • Pós-remoção (@PostRemove)
  • Pós-carregamento (@PostLoad)

A entidade que queremos monitorar deve conter a anotação @EntityListener. Veja exemplos da anotação:

  • @EntityListeners(ClasseListener.class)
  • @EntityListeners({ClasseListener1.class, ClasseListener1.class})

No exemplo que segue, temos uma entidade chamada Nota que terá seus eventos monitorados pela classe NotaEventListener.

Configurações da unidade de persistência em /META-INF

[persistence.xml]

<?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>

<!– 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.ddl-generation” value=”create-tables” /> –>
<property name=”toplink.logging.level” value=”FINEST” />
</properties>

</persistence-unit>
</persistence>

Esta entidade tem a anotação @EntityListeners onde especificamos quais classes irão monitorar os eventos ocorridos.

[Nota.java]

package com.wordpress.ericogr.entities;

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

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.EntityListeners;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;

@Entity
@Table(name = “nota”)
@EntityListeners(NotaEventListener.class)
public class Nota implements Serializable {
private static final long serialVersionUID = 1L;

private int id;
private int numero;
private int tipo;
private Date data;
private String descricao;
private Collection<ItemNota> itensNota = new ArrayList<ItemNota>();

public Nota() {
}

public Nota(int id) {
setId(id);
}

@Id
public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public String getDescricao() {
return descricao;
}

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

@OneToMany(cascade = CascadeType.ALL)
public Collection<ItemNota> getItensNota() {
return itensNota;
}

public void setItensNota(Collection<ItemNota> itensNota) {
this.itensNota = itensNota;
}

public int getNumero() {
return numero;
}

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

public int getTipo() {
return tipo;
}

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

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

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

public static long getSerialVersionUID() {
return serialVersionUID;
}
}

Entidade que representa os itens da nota fiscal.

[ItemNota.java]

package com.wordpress.ericogr.entities;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

@Entity
@Table(name = “item_nota”)
public class ItemNota implements Serializable {
private int id;
private int quantidade;
private Nota nota;

public ItemNota() {
}

public ItemNota(int id, int quantidade) {
setId(id);
setQuantidade(quantidade);
}

@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;
}

@ManyToOne
public Nota getNota() {
return nota;
}

public void setNota(Nota nota) {
this.nota = nota;
}
}

Agora a classe que “ouve” os eventos ocorridos na entidade Nota. Para podermos observar o que acontece, coloquei todos os listeners disponíveis nesta classe.

package com.wordpress.ericogr.entities;

import javax.persistence.PostLoad;
import javax.persistence.PostPersist;
import javax.persistence.PostRemove;
import javax.persistence.PostUpdate;
import javax.persistence.PrePersist;
import javax.persistence.PreRemove;
import javax.persistence.PreUpdate;

public class NotaEventListener {

@PrePersist
public void prePersistNotaEvent(Nota nota) {
System.out.println(“PrePersist: ” + nota.getDescricao());
}

@PostPersist
public void postPersistEvent(Nota nota) {
System.out.println(“PostPersist: ” + nota.getDescricao());
}

@PreUpdate
public void preUpdateNotaEvent(Nota nota) {
System.out.println(“PreUpdate: ” + nota.getDescricao());
}

@PostUpdate
public void postUpdateNotaEvent(Nota nota) {
System.out.println(“PostUpdate: ” + nota.getDescricao());
}

@PreRemove
public void preRemoveNotaEvent(Nota nota) {
System.out.println(“PreRemove: ” + nota.getDescricao());
}

@PostRemove
public void postRemoveNotaEvent(Nota nota) {
System.out.println(“PostRemove: ” + nota.getDescricao());
}

@PostLoad
public void postLoadNotaEvent(Nota nota) {
System.out.println(“PostLoad: ” + nota.getDescricao());
}
}

A classe Main é o ponto de entrada para o programa. Ela testa a inserção, remoção, atualização e carga de registros que serão monitorados pelo “listener”.

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.ItemNota;
import com.wordpress.ericogr.entities.Nota;

public class Main {

public static void main(String[] args) {
new Main();
}

@SuppressWarnings(“unchecked”)
public Main() {
EntityManagerFactory emf;
EntityManager em;

Nota nota1;
ItemNota itemNota1, itemNota2;

Query q;

emf = Persistence.createEntityManagerFactory(“testeJpaPU”);
em = emf.createEntityManager();

itemNota1 = new ItemNota(1, 120);
itemNota2 = new ItemNota(2, 151);

//nota
nota1 = new Nota(1);
nota1.setData(new Date());
nota1.setNumero(112412);
nota1.setTipo(512);
nota1.setDescricao(“teste de nota”);
nota1.getItensNota().add(itemNota1);
nota1.getItensNota().add(itemNota2);

//@PrePersist/@PostPersist
em.getTransaction().begin();
em.persist(nota1);
em.getTransaction().commit();

//@PreUpdate/PostUpdate
nota1.setDescricao(“descrição da nota fiscal”);
em.getTransaction().begin();
em.persist(nota1);
em.getTransaction().commit();

//para remover a entidade do contexto, fazendo com que
//o evento @PostLoad seja disparado.
em.clear();

//@PostLoad
q = em.createQuery(“select n from Nota n where n.numero = 112412″);
List<Nota> ns = (List<Nota>)q.getResultList();

System.out.println(“Nota ” + ns.get(0).getDescricao());

em.close();
}
}

Executando o exemplo, a saída conterá as seguintes linhas:

PrePersist: teste de nota
PostPersist: teste de nota
PreUpdate: descrição da nota fiscal
PostUpdate: descrição da nota fiscal
PostLoad: descrição da nota fiscal

O restante das linhas eu ocultei para facilitar a visualização dos eventos. Como podemos ver, é bem fácil e intuitivo usar listeners.

Enviado em Java, jpa | Tagged: , , , , , , , | 2 Comentários »

Criando relacionamentos com JPA

Publicado por ericogr em 22 Abril 2008

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.

Enviado em Java, jpa | Tagged: , , , , , , , | 2 Comentários »