Versioning EJB 3.0 with Envers

User Rating: 0 / 5

Star InactiveStar InactiveStar InactiveStar InactiveStar Inactive
 
The Envers project aims to enable easy auditing/versioning of persistent classes. This can simplify storing and retrieving historical sets of data from the DB.
Similarly to Subversion, Envers has a concept of revisions. Basically, one transaction is one revision (unless the transaction didn't modify any audited entities). As the revisions are global, having a revision number, you can query for various entities at that revision, retrieving a view of the database at that revision.

The Project Envers is available as standalone module at the JBoss site (http://www.jboss.org/envers/). Since the release 3.5 of Hibernate it is included as Hibernate module too.

Let's see a practical example. The following Entity stores the userName and password credential for users. The userName is the primary key.
Supposing you have any compelling reason to track this information, all you have to do is adding the @Audited annotation to your Entity:
package sample;

import org.hibernate.envers.Audited;
import javax.persistence.*;
 
@Entity
@Audited 
@Table( name="user")
public class User implements Serializable {

@Id
String userName;
String password;

// Getters/Setters here

@PostUpdate
public void postUpdate()
{
   System.out.println("@PostUpdate");

}
 @PostPersist
public void postPersist()
{
   System.out.println("@PostPersist");

}
 @PostRemove
 public void postRemove()
 {
    System.out.println("@PostRemove");

 }
}
Here you can notice two things: at first the @Audited annotation which specifies that the Entity will be recorder to an audit table at the end of every single transaction.
The second thing is the @Post callback methods, which are needed because Envers use Event listeners to track data changes.

From the configuration point of view, you have to add a few hibernate properties to enable Envers event listeners. You will need to add these properties either in persistence.xml (if you are using JPA) or hibernate.cfg.xml ( if you are using plain Hibernate)
<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>
Querying for Entities can be done by means of the AuditReader interface. 
In this sample Stateless SB we have added three methods: one for creating an Entity (createUser), one for updating its password (modifyUser) and a third one for retrieving a versioned User (getVersionedUser)
package sample;
import javax.ejb.*;
 
import javax.persistence.*;

import org.hibernate.envers.AuditReader;
import org.hibernate.envers.AuditReaderFactory;

import org.jboss.ejb3.annotation.LocalBinding;
@Stateless
     
public class TestEnver implements TestEnverItf {
    
    @PersistenceContext(unitName="unit1")  
    private EntityManager entityManager;
     
    public void createUser(String userName, String pwd) {
     
     User user = new User();
     user.setUserName(userName);
     user.setPassword(pwd);
     entityManager.persist(user);
 

    }
    public void modifyUser(String username, String pwd){
         

        User user = entityManager.find(User.class, username)         
        user.setPassword(pwd);
        entityManager.persist(user);
         
    }
    @Override
    public User getVersionedUser(String userName, int version) {
        AuditReader reader = AuditReaderFactory.get(entityManager);

        User user = reader.find(User.class, userName, version);
        return user;

        
    }
}
The method getVersionedUser shows how you can retrieve a versioned user. In the simplest case you have to pass three arguments to the AuditReader: the Class name, the Primary key and the versioning number.

Revsions will be persisted on the database. By default the REVINFO table will contain the list of revisions and additionally a single table for every entity audited, with the suffix _AUD. (Both these parameters are configurable anyway).

For additional information about Envers, visit the Enver Project documentation at:

http://www.jboss.org/files/envers/docs/index.html  

Related articles available on mastertheboss.com

EJB 3.0 tutorial : Session Beans

Enterprise JavaBeans (EJB) technology is a J2EE technology for de

JBoss MDB 3.0

Message-driven beans (MDBs) are stateless, server-side, transacti

JBoss EJB 3 BMT

In a Bean Managed Transaction, the code in the session or message

How do you keep your EJB callbacks separated from biz. methods?

JBoss daily recipe

How to set EJB timeout period ?

JBoss recipe of the day

How to add a Selector with MDB 3.0 ?

Message selectors allow an MDB to be more selective about the mes

Follow us on Twitter