Existem várias soluções de auditoria com hibernate e abaixo iremos apresentar apenas uma que foi encontrada na internet e adaptada.
1) Primeiro crie um projeto tapestry. Adicione as dependencias do hibernate para tapestry e do SGBD no arquivo pom.xml
2) Depois Vamos criar as entidades no pacote entities:
- Classe AuditTrail
package org.br.auditoria.entities; import java.util.Date; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; /** * Classe que representa a tabela de auditoria */ @Entity public class AuditTrail { @Id @GeneratedValue(strategy=GenerationType.IDENTITY) private Long id; private String entityId; private String entityName; private String entityProperty; private String entityPropertyOldValue; private String entityPropertyNewValue; private String operationType; private Long actorId; private Date relevanceTime; private String toString; public AuditTrail(String entityId, String entityName, String entityProperty, String entityPropertyOldValue, String entityPropertyNewValue, String operationType, Long actorId, Date relevanceTime, String toString) { super(); this.entityId = entityId; this.entityName = entityName; this.entityProperty = entityProperty; this.entityPropertyOldValue = entityPropertyOldValue; this.entityPropertyNewValue = entityPropertyNewValue; this.operationType = operationType; this.actorId = actorId; this.relevanceTime = relevanceTime; this.toString = toString; } //... GETTERS E SETTERS
- Classe Pessoa, será nossa entidade de teste a qual será auditada ela herda de AbstractEntity que possui os campo userId o qual é o id do usuário da aplicação que fica armazenado na sessão HTTP.
package org.br.auditoria.entities; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; @Entity public class Pessoa extends AbstractEntity { @Id @GeneratedValue(strategy=GenerationType.IDENTITY) private long id; private String nome; private int idade; @Override public String toString() { // TODO Auto-generated method stub return getUserId()+ " - " + getId()+" - "+getNome() + " - " + getIdade(); } //..... GETTERS E SETTERS dos atributos
- Classe AbstractEntity
package org.br.auditoria.entities; import javax.persistence.Transient; import org.apache.tapestry5.beaneditor.NonVisual; public abstract class AbstractEntity { @Transient @NonVisual private int userId; //... GETTERS E SETTERS
3) No Pacote data criamos a classe Visit que guardar alguns propriedades na sessão HTTP.
- Classe Visit:
package org.br.auditoria.data; public class Visit { private int id; private String login; //... GETTERS E SETTERS
4) No Pacote services adicione a seguinte classe, ela que que faz a auditoria no sistema:
package org.br.auditoria.services; /* | File: HibernateAuditLogListener.java | Created: Mar 3, 2008 | Author: Will Hoover */ import java.io.Serializable; import java.util.Collection; import java.util.Date; import org.br.auditoria.entities.AuditTrail; import org.hibernate.EntityMode; import org.hibernate.HibernateException; import org.hibernate.StatelessSession; import org.hibernate.cfg.Configuration; import org.hibernate.event.Initializable; import org.hibernate.event.PreDeleteEvent; import org.hibernate.event.PreDeleteEventListener; import org.hibernate.event.PreInsertEvent; import org.hibernate.event.PreInsertEventListener; import org.hibernate.event.PreLoadEvent; import org.hibernate.event.PreLoadEventListener; import org.hibernate.event.PreUpdateEvent; import org.hibernate.event.PreUpdateEventListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Audit Log Listener is used to log insert, update, delete, and load operations. Complete list of listeners/events can be obtained at <tt>org.hibernate.event.EventListeners</tt>. * * @see org.hibernate.event.EventListeners * @author whoover */ public final class HibernateAuditLogListener implements PreDeleteEventListener, PreInsertEventListener, PreUpdateEventListener, PreLoadEventListener, Initializable { private static final long serialVersionUID = 1L; private static final Logger LOG = LoggerFactory.getLogger(HibernateAuditLogListener.class); public static final String OPERATION_TYPE_INSERT = "INSERT"; public static final String OPERATION_TYPE_UPDATE = "UPDATE"; public static final String OPERATION_TYPE_DELETE = "DELETE"; /** * {@inheritDoc} */ public final void initialize(final Configuration cfg) { // } /** * Log deletions made to the current model in the the Audit Trail. * * @param event * the pre-deletion event */ public final boolean onPreDelete(final PreDeleteEvent event) { try { final Long actorId = Long.parseLong((event.getEntity().toString()).split(" - ")[0]); final Serializable entityId = event.getPersister().hasIdentifierProperty() ? event.getPersister().getIdentifier(event.getEntity(), event.getPersister().guessEntityMode(event.getEntity())) : null; final String entityName = event.getEntity().getClass().toString(); //TODO Adicionei final String entityToString = event.getEntity().toString(); final Date transTime = new Date(); // new Date(event.getSource().getTimestamp()); // need to have a separate session for audit save StatelessSession session = event.getPersister().getFactory().openStatelessSession(); session.beginTransaction(); if (LOG.isDebugEnabled()) { LOG.debug("{} for: {}, ID: {}, actor: {}, date: {}", new Object[] { entityName, entityId, actorId, transTime }); } session.insert(new AuditTrail(entityId.toString(), entityName, OPERATION_TYPE_DELETE, null, null, OPERATION_TYPE_DELETE, actorId, transTime, entityToString)); session.getTransaction().commit(); session.close(); } catch (HibernateException e) { LOG.error("Unable to process audit log for DELETE operation", e); } return false; } /** * Log insertions made to the current model in the the Audit Trail. * * @param event * the pre-insertion event */ public final boolean onPreInsert(final PreInsertEvent event) { try { final Long actorId = Long.parseLong((event.getEntity().toString()).split(" - ")[0]); final String entityId = event.getPersister().hasIdentifierProperty() ? event.getPersister().getIdentifier(event.getEntity(), event.getPersister().guessEntityMode(event.getEntity())) .toString() : ""; final String entityName = event.getEntity().getClass().toString(); final String entityToString = event.getEntity().toString(); final Date transTime = new Date(); // new Date(event.getSource().getTimestamp()); final EntityMode entityMode = event.getPersister().guessEntityMode(event.getEntity()); Object newPropValue = null; // need to have a separate session for audit save StatelessSession session = event.getPersister().getFactory().openStatelessSession(); session.beginTransaction(); for (String propertyName : event.getPersister().getPropertyNames()) { newPropValue = event.getPersister().getPropertyValue(event.getEntity(), propertyName, entityMode); // because we are performing an insert we only need to be concerned will non-null values if (newPropValue != null) { // collections will fire their own events if (!(newPropValue instanceof Collection)) { if (LOG.isDebugEnabled()) { LOG.debug("{} for: {}, ID: {}, property: {}, value: {}, actor: {}, date: {}", new Object[] { OPERATION_TYPE_INSERT, entityName, entityId, propertyName, newPropValue, actorId, transTime }); } session.insert(new AuditTrail(entityId, entityName, propertyName, null, newPropValue != null ? newPropValue.toString() : null, OPERATION_TYPE_INSERT, actorId, transTime, entityToString)); } } } session.getTransaction().commit(); session.close(); } catch (HibernateException e) { LOG.error("Unable to process audit log for INSERT operation", e); } return false; } /** * Log updates made to the current model in the the Audit Trail. * * @param event * the pre-update event */ public final boolean onPreUpdate(PreUpdateEvent event) { try { final Long actorId = Long.parseLong((event.getEntity().toString()).split(" - ")[0]); final Serializable entityId = event.getPersister().hasIdentifierProperty() ? event.getPersister().getIdentifier(event.getEntity(), event.getPersister().guessEntityMode(event.getEntity())) : null; final String entityName = event.getEntity().getClass().toString(); //TODO Adicionei final String entityToString = event.getEntity().toString(); final Date transTime = new Date(); // new Date(event.getSource().getTimestamp()); final EntityMode entityMode = event.getPersister().guessEntityMode(event.getEntity()); Object oldPropValue = null; Object newPropValue = null; // need to have a separate session for audit save StatelessSession session = event.getPersister().getFactory().openStatelessSession(); session.beginTransaction(); // get the existing entity from session so that we can extract existing property values Object existingEntity = session.get(event.getEntity().getClass(), entityId); // cycle through property names, extract corresponding property values and insert new entry in audit trail for (String propertyName : event.getPersister().getPropertyNames()) { newPropValue = event.getPersister().getPropertyValue(event.getEntity(), propertyName, entityMode); // because we are performing an insert we only need to be concerned will non-null values if (newPropValue != null) { // collections will fire their own events if (!(newPropValue instanceof Collection)) { oldPropValue = event.getPersister().getPropertyValue(existingEntity, propertyName, entityMode); if (LOG.isDebugEnabled()) { LOG.debug("{} for: {}, ID: {}, property: {}, old value: {}, new value: {}, actor: {}, date: {}", new Object[] { OPERATION_TYPE_UPDATE, entityName, entityId, propertyName, oldPropValue, newPropValue, actorId, transTime }); } session.insert(new AuditTrail(entityId.toString(), entityName, propertyName, oldPropValue != null ? oldPropValue.toString() : null, newPropValue != null ? newPropValue .toString() : null, OPERATION_TYPE_UPDATE, actorId, transTime, entityToString)); } } } session.getTransaction().commit(); session.close(); } catch (HibernateException e) { LOG.error("Unable to process audit log for UPDATE operation", e); } return false; } /** * Log the loading of the current model in the the Audit Trail. * * @param event * the pre-load event */ public final void onPreLoad(final PreLoadEvent event) { // TODO : complete load audit log if desired } }
5) No arquivo hibernate.cfg.xml adicione dentro da tag <session-factory>:
<session-factory> <!-- Acima ficará seu as properties, com informação de usuario banco de dados senha , driver, etc. --> <listener type="pre-delete" class="org.br.auditoria.services.HibernateAuditLogListener"/> <listener type="pre-update" class="org.br.auditoria.services.HibernateAuditLogListener"/> <listener type="pre-insert" class="org.br.auditoria.services.HibernateAuditLogListener"/> <listener type="pre-load" class="org.br.auditoria.services.HibernateAuditLogListener"/> </session-factory>
6) No Pacote pages crie as classes:
- Classe Index, onde será colocado a tela de login:
package org.br.auditoria.pages; import java.util.Date; import org.apache.tapestry5.annotations.Property; import org.apache.tapestry5.annotations.SessionState; import org.br.auditoria.data.Visit; /** * Start page of application auditoria. */ public class Index { @Property private String login; @Property private String senha; @SessionState private Visit visit; Class<?> onSubmit(){ if(login.equalsIgnoreCase("admin") && senha.equalsIgnoreCase("admin")){ visit.setId(1); visit.setLogin(login); return ListarPessoas.class; } else { return null; } } public Date getCurrentTime() { return new Date(); } }
- Classe ListarPessoas, onde é exibido uma grid com a lista de entidade pessoas e os links editar e deletar:
package org.br.auditoria.pages; import java.util.Date; import java.util.List; import org.apache.tapestry5.annotations.Property; import org.apache.tapestry5.annotations.SessionState; import org.apache.tapestry5.hibernate.annotations.CommitAfter; import org.apache.tapestry5.ioc.annotations.Inject; import org.br.auditoria.dao.GenericDAO; import org.br.auditoria.data.Visit; import org.br.auditoria.entities.Pessoa; import org.hibernate.Session; public class ListarPessoas { @SessionState @Property private Visit visit; @Inject private Session session; @Property private Pessoa pessoa; public Date getCurrentTime() { return new Date(); } public List<Pessoa> getPessoas(){ GenericDAO dao = new GenericDAO(session,Pessoa.class); return (List<Pessoa>)dao.getList(); } @CommitAfter void onActionFromDeletar(Long id){ GenericDAO dao = new GenericDAO(session,Pessoa.class); Pessoa p = (Pessoa)dao.getById(id); p.setUserId(visit.getId()); session.delete(p); } }
- Classe CadastroPessoa, onde a entidade pessoa pode ser cadastrada ou atualizada:
package org.br.auditoria.pages; import org.apache.tapestry5.annotations.Property; import org.apache.tapestry5.annotations.SessionState; import org.apache.tapestry5.hibernate.annotations.CommitAfter; import org.apache.tapestry5.ioc.annotations.Inject; import org.br.auditoria.dao.GenericDAO; import org.br.auditoria.data.Visit; import org.br.auditoria.entities.Pessoa; import org.hibernate.Session; public class CadastroPessoa { @SessionState private Visit visit; @Property private Pessoa pessoa; private Long pessoaId; @Inject private Session session; void onSubmit(){ gravar(); } @CommitAfter private void gravar() { pessoa.setUserId(visit.getId()); session.saveOrUpdate(pessoa); } void onActivate(Long id){ pessoaId = id; GenericDAO dao = new GenericDAO(session, Pessoa.class); pessoa=(Pessoa)dao.getById(pessoaId); } Long onPassivate(){ return pessoaId; } }
7) Na pasta src/main/webapp crile os arquivos .tml abaixo:
- Arquivo Index.tml:
<html t:type="layout" title="auditoria Index" t:sidebarTitle="Current Time" xmlns:t="http://tapestry.apache.org/schema/tapestry_5_1_0.xsd" xmlns:p="tapestry:parameter"> <!-- Most of the page content, including <head>, <body>, etc. tags, comes from Layout.tml --> <p>${message:greeting}</p> <t:form> <fieldset> Login: <t:textfield t:id="login" t:value="login" /> Senha: <t:passwordfield t:id="senha" t:value="senha" /> </fieldset> <t:submit t:id="gravar" t:value="gravar" /> </t:form> <p:sidebar> <p> Just to prove this is live: </p> <p>The current time is: ${currentTime}.</p> <p> [<t:pagelink page="Index">refresh</t:pagelink>] </p> </p:sidebar> </html>
- Arquivo CadastroPessoa.tml:
<html t:type="layout" title="Contact org.br" xmlns:t="http://tapestry.apache.org/schema/tapestry_5_1_0.xsd" xmlns:p="tapestry:parameter"> <p>Pessoa</p> <t:beaneditform object="pessoa"> </t:beaneditform> <t:pagelink t:page="listarpessoas">Listar Todos</t:pagelink> </html>
- Arquivo ListarPessoas.tml:
<html t:type="layout" title="About auditoria" xmlns:t="http://tapestry.apache.org/schema/tapestry_5_1_0.xsd" xmlns:p="tapestry:parameter"> <p>Lista de Pessoas</p> Vitis ID = ${visit.id} <t:grid source="pessoas" add="editar,deletar" row="pessoa"> <t:parameter name="editarCell"> <t:pagelink t:id="editar" t:page="CadastroPessoa" t:context="pessoa.id">Editar</t:pagelink> </t:parameter> <t:parameter name="deletarCell"> <t:actionlink t:id="deletar" t:context="pessoa.id">Deletar</t:actionlink> </t:parameter> </t:grid> <t:pagelink t:page="CadastroPessoa">Cadastrar</t:pagelink> </html>