Académique Documents
Professionnel Documents
Culture Documents
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.
51
"SUJHPt"VEJUPSJB"WBOÎBEBEF1FSTJTUÐODJBDPN)JCFSOBUF
+1"F&OWFST
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.
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
@Audited i3&7w faz a ligação com o campo de mesmo nome na tabela iSFWJOGPw.
@Entity
@Table(name = "jogador") $BNQPTEBUBCFMBDFOUSBMEFDPOUSPMFEFPQFSBÎÜFT mHVSB
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:
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.
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.
...
"org.jboss.seam.security.identity");
private Integer idUsuario;
Integer idUsuario = new Integer(identity.getPrincipal().getName());
entidadeRevisoes.setIdUsuario(idUsuario);
// get e set de idUsuario
}
}
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" />
@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.
55
"SUJHPt"VEJUPSJB"WBOÎBEBEF1FSTJTUÐODJBDPN)JCFSOBUF
+1"F&OWFST
// faça o que quiser com as informações do evento “post delete” Object[] getOldState() Retorna um vetor de onPostUpdate
// 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()
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