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 a versioned entity is modified). As revisions are global, you can have at most one revisions entity.

This entity must have at least two properties:

  • an integer-valued property, annotated with @RevisionNumber. Most often, this will be an auto-generated primary key.
  • a long-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.jboss.envers.DefaultRevisionEntity, which already has those two properties.

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.

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.jboss.envers.RevisionEntity;
import org.jboss.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.jboss.envers.example;

import org.jboss.envers.RevisionNumber;
import org.jboss.envers.RevisionTimestamp;
import org.jboss.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.jboss.envers.example;

import org.jboss.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());
    }
}

You can also download an Envers + Seam demo, where this listener is used in a wiki application.

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 _revisions_info.