Hibernate.orgCommunity Documentation

Chapter 4. Logging data for revisions

Envers provides an easy way to log additional data for each revision. You simply need to annotate one entity with @RevisionEntity, and a new instance of this entity will be persisted when a new revision is created (that is, whenever an audited entity is modified). As revisions are global, you can have at most one revisions entity.

This entity must have at least two properties:

  1. an integer- or long-valued property, annotated with @RevisionNumber. Most often, this will be an auto-generated primary key.

  2. a long- or j.u.Date- valued property, annotated with @RevisionTimestamp. Value of this property will be automatically set by Envers.

You can either add these properties to your entity, or extend org.hibernate.envers.DefaultRevisionEntity, which already has those two properties.

When using a Date, instead of a long/Long for the revision timestamp, take care not to use a mapping of the property which will loose precision (for example, using @Temporal(DATE) is wrong, as it doesn't store the time information, so many of your revisions will appear to happen at exactly the same time). A good choice is a @Temporal(TIMESTAMP).

To fill the entity with additional data, you'll need to implement the org.jboss.envers.RevisionListener interface. Its newRevision method will be called when a new revision is created, before persisting the revision entity. The implementation should be stateless and thread-safe. The listener then has to be attached to the revisions entity by specifying it as a parameter to the @RevisionEntity annotation.

Alternatively, you can use the getCurrentRevision method of the AuditReader interface to obtain the current revision, and fill it with desired information. The method has a persist parameter specifying, if the revision entity should be persisted before returning. If set to true, the revision number will be available in the returned revision entity (as it is normally generated by the database), but the revision entity will be persisted regardless of wheter there are any audited entities changed. If set to false, the revision number will be null, but the revision entity will be persisted only if some audited entities have changed.

A simplest example of a revisions entity, which with each revision associates the username of the user making the change is:

package org.jboss.envers.example;

import org.hibernate.envers.RevisionEntity;
import org.hibernate.envers.DefaultRevisionEntity;

import javax.persistence.Entity;

@Entity
@RevisionEntity(ExampleListener.class)
public class ExampleRevEntity extends DefaultRevisionEntity {
	private String username;

	public String getUsername() { return username; }
	public void setUsername(String username) { this.username = username; }
}

Or, if you don't want to extend any class:

package org.hibernate.envers.example;

import org.hibernate.envers.RevisionNumber;
import org.hibernate.envers.RevisionTimestamp;
import org.hibernate.envers.RevisionEntity;

import javax.persistence.Id;
import javax.persistence.GeneratedValue;
import javax.persistence.Entity;

@Entity
@RevisionEntity(ExampleListener.class)
public class ExampleRevEntity {
    @Id
    @GeneratedValue
    @RevisionNumber
    private int id;

    @RevisionTimestamp
    private long timestamp;

    private String username;

    // Getters, setters, equals, hashCode ...
}

An example listener, which, if used in a JBoss Seam application, stores the currently logged in user username:

package org.hibernate.envers.example;

import org.hibernate.envers.RevisionListener;
import org.jboss.seam.security.Identity;
import org.jboss.seam.Component;

public class ExampleListener implements RevisionListener {
    public void newRevision(Object revisionEntity) {
        ExampleRevEntity exampleRevEntity = (ExampleRevEntity) revisionEntity;
        Identity identity = (Identity) Component.getInstance("org.jboss.seam.security.identity");

        exampleRevEntity.setUsername(identity.getUsername());
    }
}

Having an "empty" revision entity - that is, with no additional properties except the two mandatory ones - is also an easy way to change the names of the table and of the properties in the revisions table automatically generated by Envers.

In case there is no entity annotated with @RevisionEntity, a default table will be generated, with the name REVINFO.