Vous êtes sur la page 1sur 7

artigo

José Yoshiriro Ajisaka Ramos


(jyoshiriro@gmail.com): bacharel em Sistema
de Informação (IESAM). Mestrando em
Ciência da Computação (UFPA). Instrutor na
Equilibrium Web e na UAB. Engenheiro de
software do Tribunal de Justiça do Pará. Possui
as certificações SCJP, SCWCD e SCBCD. Trabalha
com Java há 6 anos.

Márcia Amaral
(eme.amaral@gmail.com): bacharel em Ciência
da Computação (UFPA). Analista de Sistemas
do Tribunal de Justiça do Pará. "Java Girl" há
mais de 8 anos. Eterna estudante de novas
tecnologias.

Auditoria Avançada de Persistência


com Hibernate, JPA e Envers
Aprenda como implementar um robusto e completo sistema de
auditoria de persistência com o Hibernate em conjunto com o Envers

A auditoria sobre as operações de banco de dados é importantíssima, quando não,


indispensável em sistemas de porte corporativo. Essa importância surge a partir de
necessidades como correção de inconsistências de dados, detecção de incidentes
de segurança etc. Caso os requisitos de um sistema exijam muitos detalhes na
auditoria de persistência, torna-se necessário o uso de ferramentas de alto nível
para cumpri-los com eficiência. Este artigo visa apresentar o “caminho das pedras”
para implementar auditoria em persistência objeto relacional com Hibernate, JPA e
Envers, apresentando um grande leque de recursos para esse tipo de tarefa.

A uditar a persistência permite analisar operações que envolvem


o acesso a bancos de dados. Uma boa auditoria de persistência
deve fornecer informações sobre as operações nas tabelas envolvidas:
Muito provavelmente, o tipo de auditoria mais usado atualmente é o basea-
do em Stored Procedures e Triggers de bancos de dados. As técnicas aborda-
das neste artigo não entram em conflito com tais mecanismos, podendo até
t RVFNJOTFSJV FEJUPV FYDMVJVPVBUÏNFTNPDPOTVMUPVJOGPSNBÎÜFT trabalhar em conjunto.
t RVFJOGPSNBÎÜFTGPSBNFOWPMWJEBT
t DPNPFTUBWBNBOUFTFDPNPmDBSBNEFQPJT Este artigo visa apresentar uma robusta e flexível ferramenta para implemen-
t RVBOEPGPJGFJUP tação de auditoria de persistência, composta pelos frameworks Hibernate,
JPA e Envers tendo como foco principal o último, que é quem possibilita que
O nível de detalhe das informações geradas pela auditoria deve ser suficiente a tecnologia de mapeamento objeto relacional dos demais possa contar com
para que seja possível “desfazer” operações sempre que necessário. um forte sistema de auditoria.

51
"SUJHPt"VEJUPSJB"WBOÎBEBEF1FSTJTUÐODJBDPN)JCFSOBUF +1"F&OWFST

Hibernate e Envers t )JCFSOBUF&OUJUZ.BOBHFS 

 1BTTP Configuração do Hibernate. Deve-se configurar o Hibernate


O Hibernate é um framework de mapeamento objeto relacional muito
para que ele trabalhe em conjunto com o Envers. Na Listagem 1 há um
popular entre os desenvolvedores Java. Atualmente, ele pode ser usado
exemplo de como isso pode ser feito em arquivo de configuração JPA.
de duas maneiras: como “Hibernate Core” ou provedor de persistência
JPA (Java Persistence API).
Listagem 1. Configuração do Envers no persistence.xml de um projeto
Na primeira, não se depende dos recursos da JPA, podendo lançar mão Hibernate/JPA.
de vários recursos exclusivos, como Criteria, por exemplo. Os recursos
<persistence-unit ...>
exclusivos do Hibernate Core que possibilitam a implementação de ...
auditoria são os Interceptors e Events, que não fazem parte do escopo <properties>
deste artigo. <!-- configurações comuns -->

Na segunda, ele se comporta como uma implementação da JPA. A bi- <property name="hibernate.ejb.event.post-insert"
blioteca que permite que o Hibernate seja usado como provedor JPA é a value="org.hibernate.envers.event.AuditEventListener" />
“Hibernate Entity Manager”. É só com esse tipo de uso que é possível usar
o framework Envers para auditoria de persistência. <property name="hibernate.ejb.event.post-update"
value="org.hibernate.envers.event.AuditEventListener" />
O JPA é a tecnologia padrão de mapeamento objeto relacional do Java.
Na edição 37 da Mundoj, o artigo “Auditando persistência com JPA” apre- <property name="hibernate.ejb.event.post-delete"
sentou o mecanismo de Listeners de Callbacks, que permite a implemen- value="org.hibernate.envers.event.AuditEventListener" />
tação de auditoria com JPA. Neste artigo, foram abordados os pontos
fracos de se implementar auditoria com JPA, que são a não “escuta” dos <property name="hibernate.ejb.event.pre-collection-update"
Listaners aos métodos invocados de Query e a não-obtenção do estados value="org.hibernate.envers.event.AuditEventListener" />
“antes e depois” dos objetos envolvidos na operação de persistência.
Conforme será apresentado nas próximas seções, o Envers supera essas <property name="hibernate.ejb.event.pre-collection-remove"
limitações e ainda oferece funcionalidades exclusivas. value="org.hibernate.envers.event.AuditEventListener" />

O Envers é um framework que permite criar auditoria através do controle <property name="hibernate.ejb.event.post-collection-recreate"
de versões de persistência em mapeamentos objetos relacionais feitos value="org.hibernate.envers.event.AuditEventListener" />
com o Hibernate como provedor JPA. A grande inovação é a geração e
alimentação automáticas de tabelas que permitem o controle de versões </properties>
dos conteúdos das tabelas mapeadas. Cada vez que uma tabela sofre al- </persistence-unit>
terações em seus registros, uma nova “versão” dela é gerada pelo Envers.
Assim, este framework torna opcional a criação de tabelas e programa- Em seu modo padrão de configuração (o que está sendo apresentado até
ção de módulos de sistemas para armazenar as informações de auditoria. agora), o Envers precisa de uma tabela auxiliar para cada tabela “oficial”
do sistema cuja classe de entidade for marcada para auditoria, além de
O Hibernate e o Envers são mantidos pela JBoss.org e, em 30 de outubro uma tabela auxiliar única por todo o projeto. Por isso, é uma boa prática
de 2008, o Envers tornou-se um módulo do Hibernate. Todavia, sua bi- configurar a criação automática de tabelas com a propriedade “hbm2ddl.
blioteca ainda não vem junto do Hibernate, sendo necessário fazer seu auto” do Hibernate. A figura 1 mostra como seria um DER preparado para
download separadamente. Segundo o site da JBoss, o Hibernate 3.5 já o trabalho com o Envers se houvessem apenas duas tabelas no sistema
trará as bibliotecas do Envers juntos das suas, assim como já ocorre com (tabela “jogador” e tabela “time_futebol”). Detalhes sobre essas tabelas
C3P0, Ehcache e outros frameworks. Nessa versão, provavelmente será estão na seção “Armazenamento das Informações pelo Envers”.
possível usar o Envers também em conjunto com o Hibernate Core e não
apenas com o Hibernate como provedor JPA.

Criando auditoria de persistência com o


framework Envers
Até o fechamento desta edição, a versão atual do Envers era 1.2.1 GA,
compatível com as versões 3.3.x do Hibernate. É com estas configurações
que este artigo irá apresentar sua proposta. A seguir, um passo-a-passo
de como configurar o Envers em seu projeto.
'JHVSB%&3QBSBEVBTUBCFMBT iKPHBEPSwFiUJNF@GVUFCPMw
NBJTBTUBCFMBTEFVTPEP&OWFST
"TUBCFMBTiKPHBEPS@BVEwFiUJNF@GVUFCPM@BVEwTÍP SFTQFDUJWBNFOUF TVBTUBCFMBTEFBVEJUPSJB
1BTTP Instalação de bibliotecas. A biblioteca do Envers é composta individual.
de um único arquivo, o “envers-X.ga-hibernate-3.Y.jar”, em que X e Y são
as versões do Envers e Hibernate da biblioteca, respectivamente. Outras A tabela “revinfo” é usada por todas as tabelas com sufixo “_aud”. E existe
bibliotecas necessárias são: uma tabela com esse sufixo para cada tabela cuja entidade for marcada
t )JCFSOBUF para auditoria com o Envers (veja como fazer isso no próximo passo).

52 www.mundoj.com.br
"SUJHPt"VEJUPSJB"WBOÎBEBEF1FSTJTUÐODJBDPN)JCFSOBUF +1"F&OWFST

1BTTP Indicar quais tabelas serão auditadas. Com uma simples ano- $BNQPTEBTUBCFMBTJOEJWJEVBJTEFBVEJUPSJB mHVSB

tação é possível indicar quais classes de entidade fiquem sob a escuta do


Envers e passarão a fazer parte de seu controle de versão. É a anotação @ As tabelas de auditoria individuais, as que terminam com “_BVE” pos-
import org.hibernate.envers.Audited. Na Listagem 2 há um exemplo de suem os mesmos campos das tabelas as quais auditam e mais estes:
como usá-la.
i3&75:1&w indica o tipo de operação, em que:

Listagem 2. Marcando a classe de entidade “Jogador” para auditoria com


o Envers. iwindica novo registro
iw indica edição de registro
import org.hibernate.envers.Audited; iw indica exclusão

@Audited i3&7w faz a ligação com o campo de mesmo nome na tabela iSFWJOGPw.
@Entity
@Table(name = "jogador") $BNQPTEBUBCFMBDFOUSBMEFDPOUSPMFEFPQFSBÎÜFT mHVSB

public class Jogador implements java.io.Serializable {


// campos e métodos...
O Envers usa uma tabela para centralizar o controle de operações, a “re-
}
WJOGP”. Ela possui os seguintes campos:

Se estes três passos forem executados corretamente, sua aplicação já i3&7w faz a ligação com as tabelas de auditoria individuais (as de final
estará sob a auditoria e controle de versão de tabelas do Envers. “_BVE”). O valor de REV é único por aplicação que usa o Envers, indepen-
dentemente de quantas tabelas estão sendo auditadas.
Armazenamento das informações pelo Envers
i3&7545.1w indica o momento exato (o “timestamp”) em que a ope-
Vamos supor que na tabela “time_futebol” foram feitas as seguintes ração ocorreu.
operações a partir de um projeto que usa Hibernate, JPA e Envers confi-
gurado no modelo padrão:

Recursos avançados de auditoria com Envers


1. dois times foram inseridos;
2. o registro de um time sofreu alterações; Os recursos abordados até aqui são suficientes para um sistema mínimo de
3. o registro de um time foi excluído. auditoria, mas não para um sistema profissional em um ambiente corporati-
vo. Para tal, é necessário saber pelo menos que usuário foi responsável pelas
As figuras 2 e 3 mostram como ficariam as tabelas “time_futebol_aud” e
operações realizadas. É hora de "lançar mão" do recurso de escuta de revisão
“revinfo”, respectivamente.
(Revision Listener) do Envers e criar uma classe para ser a “entidade de revisão”
do projeto.

Uma “entidade de revisão” é a classe que será usada para representar a tabela
“revinfo”, portanto, deve estar mapeada como uma entidade JPA. Ela pode
ser criada estendendo a classe org.hibernate.envers.DefaultRevisionEntity e
criando os campos que achar importante para a auditoria. Essa classe também
'JHVSB  5BCFMB iUJNF@GVUFCPM@BVEw BQØT BMHVNBT PQFSBÎÜFT B QBSUJS EB BQMJDBÎÍP DPN deve ser anotada com @org.hibernate.envers.RevisionEntity para indicar qual
)JCFSOBUFF&OWFST
classe de escuta de revisões (Revision Listener) irá gerenciá-la. Há um exemplo
de entidade de revisão na Listagem 3.
Se você preferir que sua tabela central de auditoria não se chame “revinfo”,
basta usar outra palavra no atributo “name” na anotação @Entity ou mesmo
omiti-lo para aproveitar o nome da própria classe.

Caso não deseje estender a classe DefaultRevisionEntity para a entidade de


revisão, você deve criar pelo menos dois campos devidamente encapsulados
'JHVSB5BCFMBiSFWJOGPwBQØTBMHVNBTPQFSBÎÜFTBQBSUJSEBBQMJDBÎÍPDPN)JCFSOBUFF&OWFST
(com seus “get” e “set”):
Uma característica fantástica deste framework é que ele escuta tanto as
operações feitas os métodos CRUD da interface EntityManager (persist, 1. do tipo Integer ou int, anotado com @org.hibernate.envers.Revision-
merge ,remove e find) quanto às operações feitas pelo uso do método Number;
“executeUpdate()” da interface Query. 2. do tipo Long ou long, anotado com @org.hibernate.envers.RevisionTi-
mestamp.
Como o leitor deve ter percebido, o Envers torna muito produtiva a cria-
ção de auditoria ao acesso a bancos de dados, uma vez que ele assume Dependendo do tipo e da necessidade de um sistema, uma entidade de
a responsabilidade pelas tabelas de auditoria as quais formam históricos revisão pode conter campos como um número IP, um “host name”, o sistema
parecidos com o que vemos em sistemas de controle de versão de arqui- operacional do usuário etc. A Listagem 4 mostra um exemplo de classe Revi-
vos, como CVS e Subversion, por exemplo. sion Listener.

53
"SUJHPt"VEJUPSJB"WBOÎBEBEF1FSTJTUÐODJBDPN)JCFSOBUF +1"F&OWFST

Listagem 3. Exemplo de classe de entidade de revisão. Listagem 5. Exemplo de recuperação de identificador usando o Seam.

import org.hibernate.envers.RevisionEntity; import org.jboss.seam.security.Identity;

import org.hibernate.envers.DefaultRevisionEntity; import org.jboss.seam.Component;

...

@Entity(name="revinfo") public void newRevision(Object revisionEntity) {


@RevisionEntity(AuditoriaFutebolListener.class) // classe de escuta de revisões ...
public class ExampleRevEntity extends DefaultRevisionEntity { Identity identity = (Identity) Component.getInstance(

"org.jboss.seam.security.identity");
private Integer idUsuario;
Integer idUsuario = new Integer(identity.getPrincipal().getName());

entidadeRevisoes.setIdUsuario(idUsuario);
// get e set de idUsuario
}
}

Listagem 6. Exemplo de recuperação de identificador usando o Struts2.


Listagem 4. Exemplo de classe de escuta de revisões.
import com.opensymphony.xwork2.ActionContext;
import org.hibernate.envers. RevisionListener;
...

@Entity(name="revinfo") public void newRevision(Object revisionEntity) {


@RevisionEntity(AuditoriaFutebolListener.class)
...
public class AuditoriaFutebolListener extends RevisionListener {
Integer idUsuario = (Integer)ActionContext.getContext().getSession().
public void newRevision(Object revisionEntity) {

EntidadeRevisoes entidadeRevisoes = (EntidadeRevisoes) revisionEntity; get("idUsuarioAtual");

Integer idUsuario = ... ; //Recuperação do identificador do usuário


entidadeRevisoes.setIdUsuario(idUsuario);
// conforme a tecnologia usada
}
entidadeRevisoes.setIdUsuario(idUsuario);

} Note que esse recurso não é indicado para estudar a operação em si


} se foi inclusão, exclusão ou alteração. O método newRevision(Object
revisionEntity) é o único método da interface org.hibernate.envers.
RevisionListener. O que interessa nesse tipo de classe é apenas atribuir
informações extras para o registro da operação. Para escuta das opera-
ções CRUD, deve-se usar o recurso de escuta de eventos do Envers, que
$JDMPEFWJEBEFVNBDMBTTFEFFTDVUBEFSFWJTÜFT 3FWJTJPO-JTUFOFS

será apresentado na próxima seção.


Uma classe que implementa PSHIJCFSOBUFFOWFST3FWJTJPO-JTUFOFS
possui uma instância Á/*$" por &OUJUZ.BOBHFS'BDUPSZ. Por isso, mui- Recursos de flexibilização e personalização
to cuidado com o uso de variáveis de instância. da auditoria com Envers
O Envers tem uma “proposta pronta” de arquitetura de tabelas para pro-
ver auditoria e versionamento de persistência. É uma arquitetura muito
No exemplo da Listagem 4 existem várias formas de recuperar o identi- inteligente e organizada que permite certas customizações como vistas
ficador do usuário, dependo da tecnologia usada. Na Listagem 5 há um no tópico anterior. Todavia, pode ser necessário que a arquitetura de ta-
exemplo de como fazer isso com o framework JBoss Seam (através de belas de auditoria seja diferente da proposta pelo Envers. Também pode
busca de componente) e na Listagem 6, de como fazer com o Apache ser necessário usar outros recursos de registro de auditoria, como envio
Struts2 (através de busca de atributo no escopo de sessão). Esse tipo de de e-mails e criação de “logs” em arquivos-texto, por exemplo.
classe tem comportamento parecido com o dos Interceptors do EJB3 e
do Struts2, afinal lida com uma “responsabilidade transversal”, no caso, a O mecanismo do Envers que permite esse tipo de flexibilização é a escuta
auditoria de persistência. de eventos de auditoria (Audit Event Listener).

54 www.mundoj.com.br
"SUJHPt"VEJUPSJB"WBOÎBEBEF1FSTJTUÐODJBDPN)JCFSOBUF +1"F&OWFST

Escuta de eventos de auditoria (Audit Event Listener) Listagem 8. Configuração do Envers no persistence.xml de um projeto
Hibernate/JPA.
Para poder usar o Envers em seu modo “padrão”, é preciso configurá-lo no
JPA como visto no 2º passo do tópico “Criando auditoria de persistência <persistence-unit ...>
com o framework Envers”. O que é feito na verdade é o mapeamento dos ...
mecanismos de escuta de eventos de auditoria padrão do Envers. Para <properties>
personalizar essas escutas, basta criar uma ou mais classes que estendam <!-- configurações comuns -->
org.hibernate.envers.event.AuditEventListener (exemplo na Listagem 7)
e indicá-las na configuração do JPA (exemplo na Listagem 8). <property name="hibernate.ejb.event.post-insert"
value="mj.projetoFutebol.listeners.FutebolListener" />

Listagem 7. Exemplo de classe de escuta de eventos de auditoria personalizada. <property name="hibernate.ejb.event.post-update"


value="mj.projetoFutebol.listeners.FutebolListener " />
package mj.projetoFutebol.listeners;

import org.hibernate.envers.event.AuditEventListener; <property name="hibernate.ejb.event.post-delete"


... value="mj.projetoFutebol.listeners.FutebolListener " />
public class FutebolListener extends AuditEventListener{
@Override </properties>
public void onPostInsert(PostInsertEvent event) {
</persistence-unit>
// faça o que quiser com as informações do evento “post insert”
}

@Override
public void onPostDelete(PostDeleteEvent event) { 4FNNVEBOÎBT TFNWFSTJPOBNFOUP TFNFTDVUB
// faça o que quiser com as informações do evento “post delete""
} O Envers possui um mecanismo de prevenção de criação de versões
inúteis nas tabelas de auditorias individuais. Se for feito um pedido de
@Override
atualização através do método NFSHF() ou de uma query +12- e os
public void onPostUpdate(PostUpdateEvent event) {
// faça o que quiser com as informações do evento “post update” valores enviados forem os mesmos dos que já estão no banco de dados
} para o registro envolvido na operação, não será gerada uma nova versão
} da tabela em sua tabela de auditoria individual.

E todos os mecanismos de escuta do Envers também são ignorados


Todos os métodos de escuta de eventos de auditoria padrão do Envers quando é feito um pedido de atualização que contém dados iguais aos
possuem como parâmetro um objeto que estende org.hibernate.event. já persistidos.
AbstractEvent. Esses objetos de evento permitem recuperar várias informa-
ções pertinentes sobre o evento e a entidade envolvida nele. Os principais
métodos desses objetos de eventos estão na tabela 1. A Listagem 9 possui
um pequeno exemplo de como usar alguns do métodos dessa tabela. A classe PSHIJCFSOBUFFOWFST"VEJU3FBEFS possui os recursos para
recuperação de versões de dados de tabelas auditadas. Seus métodos
Se for invocada a versão da superclasse dos métodos de escuta de eventos,
estão na tabela 2.
o Envers tenta fazer seu procedimento padrão para a operação (registros
numa tabela “_aud” e na tabela central de controle de revisões). Assim, Existem duas outras formas de consultar as informações de auditoria
caso seja necessário apenas acrescentar funcionalidades adicionais ao geradas no banco de dados pelo Envers. Ambas utilizam a interface
Envers (como envio de e-mail, por exemplo) sem alterar seu mecanismo PSHIJCFSOBUFFOWFSTRVFSZ"VEJU2VFSZ. Essa interface permite fazer
padrão de armazenamento de dados de auditoria de persistência. Por consultas mais complexas em cima dos dados que foram armazenados.
exemplo: se você quiser que toda vez que um registro de determinada As queries do Envers são semelhantes às queries do Hibernate Criteria,
tabela seja excluído o administrador do sistema receba um e-mail, mas facilitando a vida dos leitores que já têm familiaridade com a interface
deseja manter a auditoria padrão do Envers, basta implementar o código PSHIJCFSOBUF$SJUFSJB.
de envio de e-mail no método “onPostDelete(PostDeleteEvent event)” e
A primeira forma é consultar uma entidade em uma determinada revi-
depois chamar “super.onPostDelete(event)”.
são. A segunda é consultar as revisões de uma determinada entidade. A
Listagem 11 mostra um exemplo simples de uma consulta de entidade
Recuperação dos dados de auditoria gerados dada uma revisão.
pelo Envers
A consulta da Listagem 11 retorna uma lista de jogadores na revisão
"revisionNumber" e cuja propriedade "nome_em_campo" tem o valor
Além de assumir o “trabalho pesado” pela alimentação das tabelas de "Ronaldo".
auditoria, o Envers oferece um mecanismo robusto de recuperação de
versões das tabelas. Na Listagem 10 há um exemplo de recuperação de A Listagem 12 possui um exemplo simples de consulta a uma das revi-
versão com o uso da classe org.hibernate.envers.AuditReader. sões de uma entidade com AuditQuery.

55
"SUJHPt"VEJUPSJB"WBOÎBEBEF1FSTJTUÐODJBDPN)JCFSOBUF +1"F&OWFST

Listagem 9. Exemplo de classe de escuta de eventos de auditoria Método Descrição Eventos


personalizada. Object getEntity() Retorna a instância da onPostInsert
entidade envolvida no onPostUpdate
public class FutebolListener extends AuditEventListener{ onPostDelete
evento. No caso de “onPos-
@Override tUpdate()”, os campos da
public void onPostInsert(PostInsertEvent event) { entidade recuperada por
Object entidade = event.getEntity(); este método vêm com os
valores alterados.
Serializable chave = event.getId();
Java.io.Serializable event. Retorna a chave da onPostInsert
// faça o que quiser com as informações do evento “post insert”
getId() entidade envolvida no onPostUpdate
}
evento. onPostDelete

Object[] getState() Retorna um vetor de onPostInsert


@Override objetos com os valores dos onPostUpdate
public void onPostDelete(PostDeleteEvent event) { campos da entidade já sob
Object[] estadoDeletado = event.getDeletedState(); os efeitos do evento.

// faça o que quiser com as informações do evento “post delete” Object[] getOldState() Retorna um vetor de onPostUpdate

} objetos com os valores dos


campos da “versão antiga”
da entidade (valores de an-
@Override tes de sofrer atualização).
public void onPostUpdate(PostUpdateEvent event) { Assim pode-se ter acesso
Object[] estadoNovo = event.getState(); ao registro “como estava”
Object[] estadoAntigo = event.getOldState(); antes da atualização.

// faça o que quiser com as informações do evento “post update” Object[]getDeletedState() Equivale ao getState(), onPostDelete
porém só se aplica à escuta
}
de “post-delete”
org.hibernate.collection. Retorna a coleção de onPostRecreateCollection
@Override onPreRemoveCollection
PersistentCollection instâncias das entidades
public void onPreUpdateCollection(PreCollectionUpdateEvent event) { envolvidas no evento. onPreUpdateCollection
PersistentCollection colecaoAfetada = event.getCollection(); getCollection()

Serializable chaveDaEntidadeQuePossuiAColecao = Serializable getAffectedOw- Retorna a chave da onPostRecreateCollection


nerIdOrNull() entidade à qual a coleção onPreRemoveCollection
event.getAffectedOwnerIdOrNull();
pertence. onPreUpdateCollection
String nomeEntidadeQuePossuiAColecao =
String getAffectedOwnerEn- Retorna o nome da onPostRecreateCollection
event.getAffectedOwnerEntityName(); onPreRemoveCollection
tityName entidade à qual a coleção
// faça o que quiser com as informações do evento “pre update collection” pertence. onPreUpdateCollection
} 5BCFMB1SJODJQBJTNÏUPEPTEPTFWFOUPTEFFTDVUBEFBVEJUPSJBEP&OWFST
}

Listagem 12. Recuperando uma determinada versão de registro com AuditQuery.


Listagem 10. Recuperando uma determinada versão de registro da tabela
“time_futebol”, sob a auditoria do Envers. Number revision = (Number) getAuditReader().createQuery()
.forRevisionsOfEntity(Jogador.class, false, true)
import org.hibernate.envers.AuditReader; .setProjection(AuditEntity.revisionNumber().min())
import org.hibernate.envers.AuditReaderFactory; .add(AuditEntity.id().eq(idjogador))
.add(AuditEntity.revisionNumber().gt(12))
public void recuperarTimeDeAuditoria(Integer idTime, Integer versao) { .getSingleResult();
EntityManager em = .... ;
AuditReader leitorAuditoria = AuditReaderFactory.get(em);
TimeFutebol timeRecuperado = leitorAuditoria.find(TimeFutebol.class, A consulta da Listagem 12 retorna a menor versão na qual uma entidade
idTime, versao); da classe Jogador de id "idjogador" foi alterada, depois da versão 12.
}
A classe AuditQuery tem uma série de métodos que permitem especi-
ficar restrições, fazer ordenação, limitar o número de resultados, assim
Listagem 11. Recuperando uma lista de versões de registro com AuditQuery. como também é possível fazer uso de agregações e projeções.

List jogadores = getAuditReader().createQuery(). Abordar todos os seus métodos e combinações tornaria este trabalho
forEntitiesAtRevision(Jogador.class, revisionNumber)
muito extenso. Por isso encorajamos o leitor a consultar sua documenta-
.add(AuditEntity.property("nome_em_campo").eq("Ronaldo"))
.getResultList(); ção da API (link nas referências) a fim de conhecer melhor os métodos e
as possibilidades de utilização.

56 www.mundoj.com.br
"SUJHPt"VEJUPSJB"WBOÎBEBEF1FSTJTUÐODJBDPN)JCFSOBUF +1"F&OWFST

Método Descrição Listagem 13. Recuperando instâncias de objetos mapeados para chaves
estrangeiras de entidades recuperadas pelo controle de versão do Envers.
T find(Class<T> cls, Object prima- Recupera uma determinada
ryKey, Number revision) versão (parâmetro “revision”) import org.hibernate.Hibernate;
de certo registro (parâmetro
“primaryKey”) de uma
determinada classe de public void recuperarJogadorDeAuditoria(Integer idJogador, Integer versao) {
entidade (parâmetro “cls”). EntityManager em = .... ;
T findRevision(Class<T> cls, Number Recupera uma determinada AuditReader leitorAuditoria = AuditReaderFactory.get(em);
revision) versão (parâmetro “revision”)
de certo registro (identificado Jogador jogadorRecuperado = leitorAuditoria.find(Jogador.class,
pelo parâmetro “prima- idJogador, versao);
ryKey”) de uma determinada
// objeto em “Lazy eterno”
classe de entidade (parâmetro
“cls”). TimeFutebol timeRecuperado = jogadorRecuperado.getTimeFutebol();

T getCurrentRevision(Class<T> Recupera a versão atual da // única maneira de fazer o objeto ser preenchido
revisionEntityClass, boolean persist) entidade da classe indicada Hibernate.initialize(timeRecuperado);
(parâmetro “revisionEn-
tityClass”). O parâmetro }
“persist” indica se o registro
retornado deve ou não ser
salvo no banco de dados caso
Impacto na performance com uso do Envers
ainda não esteja efetivamente
salvo.
Um trabalho de estudo do impacto na performance em uma apli-
cação com uso de Envers seria muito longo, mas, por dedução,
java.util.List<Number> Retorna a lista de versões acredita-se que o impacto é diretamente proporcional ao número
getRevisions(Class<?> cls, Object existentes para um de tabelas marcadas para auditoria e ao número de entidades en-
primaryKey) determinado registro volvidas por operação.
(parâmetro “primaryKey”) de
uma determinada classe de Para cada entidade mapeada para auditoria com Envers, a cada
entidade (parâmetro “cls”). operação interceptada por ele, um método de escuta (seção “Escuta
de eventos de auditoria”) é invocado pelo menos uma vez. Caso a
Number Retorna a última versão de entidade possua mapeamentos de um para muitos ou de muitos
getRevisionNumberForDate(java.util. uma determinada data. para muitos em cascata com outras entidades também mapeadas
Date date) para auditoria, o número de chamadas aos métodos de escuta é
java.util.Date Retorna a data de uma deter- proporcional ao número de objetos envolvidos na operação.
getRevisionDate(Number revision) minada versão de entidade
Portanto, é uma boa prática evitar o mapeamento de todas as
(parâmetro “revision”).
classes de entidades e grandes projetos para evitar grandes perdas
5BCFMB.ÏUPEPTEFPSHIJCFSOBUFFOWFST"VEJU3FBEFS de performance. Porém, se os requisitos exigirem isso, uma menor
performance é um preço a se pagar.
Pontos fracos da auditoria com Hibernate e
Envers Considerações finais

Existe uma informação equivocada na documentação da API do Neste artigo foi abordado o framework Envers para auditoria de persis-
Envers. Nela diz que o método find(Class<T> cls, Object prima- tência com o Hibernate. Foram mostrados a configuração básica, alguns
ryKey, Number revision) da classe AuditReader retorna “null” caso aspectos avançados e algumas dificuldades no uso deste framework. Os
seja passada como parâmetro uma versão inexistente na tabela de recursos oferecidos pelo Envers para auditoria com Hibernate e JPA são
auditoria individual da tabela. Todavia o que ocorre nesse caso é bem variados e poderosos, portanto, espera-se que este artigo estimule
que o método retorna a última versão da entidade. a criação de sistemas de auditoria de persistência robustos que são im-
portantíssimos para a maioria dos tipos de sistema e indispensáveis em
Nos métodos de recuperação de versões anteriores das entidades sistemas corporativos.
sob o controle de versão, os campos mapeados para objetos de ou-
tras entidades não são recuperados. É como se estivessem em modo Referências
“Lazy” eternamente, pois o Hibernate não consegue recuperar seus t 4JUFPmDJBMEP&OWFSTIUUQKCPTTPSHFOWFST
campos em runtime. Para isso, é preciso uma “técnica alternativa” t 4JUFPmDJBMEP)JCFSOBUFIUUQXXXIJCFSOBUFPSH
através do uso de recursos específicos do Hibernate, como mostra t 4JUFPmDJBMEP)JCFSOBUF&OUJUZ.BOBHFSIUUQTFOUJUZNBOBHFSIJCFSOBUFPSH
t )JCFSOBUFJOBDUJPO FEJUPSB.BOOJOH1VCMJDBUJPOT
a Listagem 13.

57

Vous aimerez peut-être aussi