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.