/home/ericogr/blog

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

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.

2 Respostas para “Usando Callback Listeners com JPA”

  1. Rodrigo Steinhorst disse

    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 disse

    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

XHTML: Você pode usar estas tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <pre> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>