Auditoria com Hibernate

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>
Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License