/home/ericogr/blog

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

Usando Callback Listeners com JPA

Posted by 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”&gt;
<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.

2 Respostas to “Usando Callback Listeners com JPA”

  1. Rodrigo Steinhorst said

    Parabens pelo blog…. muito bom o exemplo…n cheguei a testar, mas já me deu uma idéia boa de como utilizar. Estou estudando JPA e esses exemplos são muito bons.

  2. Ricardo said

    Ola, bom dia!!
    primeiramente gostaria de parabenizar pelo artigo muito bem elaborado.
    Gostaria de tirar uma duvida, tenho uma aplicacao JSF + Jpa + EJB3. Minhas classes modelo e os EntityListener estao no pacote EJB e em quase todas classes modelo da minha aplicação serão requiridos Id do usuario e da Empresa, para isso gostaria de utilizar o EntityListener para preencher essas informacoes automaticamente isolando essa regra de negocio. Porem, nao estou sabendo como resgatar usuario que esta na sessao do lado do JSF e jogar no entityListener sem que crie acoplamento.

    Voce ja passou por isso ou tem alguma sugestao de como resolver?

    desde ja, obrigado!

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: