Auditing changes using Hibernate Envers

Since v5.9 you can also use the new AccessTracker of OpenXava

Auditing Basics

Auditing can add a very powerful component to your OpenXava project using the @Audited mechanism. For many legal, regulatory and corporate-requirement reasons, your application will need to log any changes to the database, beyond whatever logging/auditing capability may be in your database itself.

Since v5.4 OpenXava has added "Hibernate Envers" API by default. For versions 5.3.* the API must be included in the classpath. The v5.3 had a bug that is resolved in this discussion.

This article is a basic intro to using this capability in OpenXava 5.3 or higher.

Configuration

Essentially, OpenXava uses the existing Hibernate envers mechanism to perform auditing. As of OpenXava 5.3 or higher , there are a minimum of steps to configure and use auditing. First, though, a couple of notes:

1 - You can specify to audit individual fields within a class or an entire class itself. Just place the @Audited tag immediately before the item to be audited.
2 - Be VERY aware, though, that any other object which references the audited field must also be audited. This includes super-classes, such as those "extends"ed.

First, perform the step below:

Add the fields below to the persistence.xml of your project:
<properties>
...
    <property name="hibernate.hbm2ddl.auto" value="update"/>
...
    <property name="org.hibernate.envers.audit_strategy"
         value="org.hibernate.envers.strategy.ValidityAuditStrategy"/>
    <property name="org.hibernate.envers.audit_strategy_validity_store_revend_timestamp"
         value="true"/>
...
</properties>
It is possible to configure various aspects of Hibernate Envers behavior, You can check the documentation about this.

The next step is to add @Audited to, again, either an individual field or entire class, such as:

public class Invoice {
@Audited Audits invNumber field
private int invNumber;

or

@Audited Audits invoice class
public class Invoice {
private int invNumber;

Usage

Presuming that you are using Eclipse, you'll immediately notice that the @Audited tag is flagged red-X. That's because the missing Hibernate envers package has not been imported. Highlight @Audited and select to include (or do it manually/via whatever other tool you may be using) the package:

import org.hibernate.envers.*;

Create and test your package! After the first creation/modification event, you should be able to launch your DB admin tool and check the columns of your DB. For each table/entity, there should be a corresponding xxx_aud table and a table called revinfo This table logs revision info for the changes.

Until v5.2.1

Add in persistence.xml:
<properties>
...
   <property name="hibernate.ejb.event.post-insert"
       value="org.hibernate.ejb.event.EJB3PostInsertEventListener,org.hibernate.envers.event.AuditEventListener" />
   <property name="hibernate.ejb.event.post-update"
       value="org.hibernate.ejb.event.EJB3PostUpdateEventListener,org.hibernate.envers.event.AuditEventListener" />
   <property name="hibernate.ejb.event.post-delete"
       value="org.hibernate.ejb.event.EJB3PostDeleteEventListener,org.hibernate.envers.event.AuditEventListener" />
   <property name="hibernate.ejb.event.pre-collection-update"
       value="org.hibernate.envers.event.AuditEventListener" />
   <property name="hibernate.ejb.event.pre-collection-remove"
       value="org.hibernate.envers.event.AuditEventListener" />
   <property name="hibernate.ejb.event.post-collection-recreate"
       value="org.hibernate.envers.event.AuditEventListener" />
...
</properties>
In build.xml of OpenXava project modify the hibernatetool classname adding Envers, as following:
<taskdef name="hibernatetool"
    classname="org.hibernate.tool.ant.EnversHibernateToolTask"
    classpathref="jpatoolslib"/>
Create the RevEntity class to indicate that the revision will contain user name.
package org.openxava.yourproject.auditing;
 
import javax.persistence.*;
import org.hibernate.envers.RevisionEntity;
import org.hibernate.envers.DefaultRevisionEntity;
 
 
@Entity
@RevisionEntity(ListenerUsuario.class)
public class RevEntity extends DefaultRevisionEntity{
 
    private String username;
    public String getUsername() { return username; }
    public void setUsername(String username) { this.username = username; }
 
}
Create the UserListener to capture the user that is modifying.
package org.openxava.yourproject.auditing;
 
import org.hibernate.envers.*;
import org.openxava.util.*;
 
public class UserListener implements RevisionListener{
 
    public void newRevision(Object revisionEntity) {
        RevEntity reventity = (RevEntity) revisionEntity;
        reventity.setUsername(getCurrentUser());
    }
 
    public static String getCurrentUser() {
        if (Users.getCurrent() != null) {
            return Users.getCurrentUserInfo().getGivenName() + " " +
            Users.getCurrentUserInfo().getFamilyName();
        } else {
            return "";
        }
    }
}
 
Finally add to the class you want to audit: @Audited

Generate the database and deploy the application.