How to Use Envers4 with Hibernate4 and Grails 2.3.0

Grails

A few days ago, the new Grails 2.3.0 was released. One of the new features is the support of Hibernate 4. While it is still not the latest release, the now supported Hibernate 4.1 has already seen some improvements in its audit module (previously known as Envers).

Envers

Envers adds the ability to version/audit your persistent classes very easily, simply by using the annotation @Audited.

Practically,  with Envers all previous versions of data can be kept. So over the time, a database with the complete historical data set and references to every change of the data will be available. Keeping a historical dataset for auditing reason is a constant requirement in my projects. Therefore the auditing capabilities of Envers would be of great help.

Lucas Ward and others did an excellent job providing an Envers plugin for Grails. Matt Rapcynski blogs about using Envers with Grails without a plugin. Both solutions support Hibernate3 and earlier versions of Grails.

Inspired by Lucas and Matt’s work, I wanted to know whether we can use Envers4 with Hibernate4 and the newest Grails 2.3.0.

The following three steps are:

  • use Hibernate4 in Grails 2.3.0
  • enable Envers4
  • use auditing and version

So here we go.

Step 1 – Enable Hibernate4 in Grails

Personally I use GGTS 3.0, the eclipse based Grails/Groovy Toolsuite. Download and install GGTS 3.0 and Grails 2.3.0.

Create a vanilla Grails 2.3.0 project and change the default configuration files, so hibernate4 will be used.

Edit BuildConfig.groovy

// BuildConfig.groovy
....
plugins {
   // plugins needed at runtime but not for compilation
   // runtime ":hibernate:3.6.10.1" // or
   runtime ":hibernate4:4.1.11.1"
}
...

Edit DataSource.groovy

// DataSource.groovy
....
hibernate {
   cache.use_second_level_cache = true
   cache.use_query_cache = false
   // cache.region.factory_class = 'net.sf.ehcache.hibernate.EhCacheRegionFactory' // Hibernate 3
   cache.region.factory_class = 'org.hibernate.cache.ehcache.EhCacheRegionFactory' // Hibernate 4
}

Create a domain class Customer.groovy

// Customer.groovy
package grailshibernateenvers4

class Customer {
Long id
String name
String phone
static constraints = {
       name blank: false, nullable: false
       phone blank: false, nullable: false
    }
}

Add some data to the customer domain …

Next we need to create a persistent class and add some data to it.
Bootstrap.groovy is a good place for this.

// Bootstrap.groovy
import grailshibernateenvers4.Customer
class BootStrap {
   def init = { servletContext ->
      def customer = new Customer()
      customer.name = "Muller"
      customer.phone = "01234"
      assert customer.save(flush: true)
   }
   ...
}

Now it’s time to run the grails app and check the database …
Database Customer with Envers tables

Ok, every is fine – the data is stored into database – and we can proceed by enabling Envers4.

Step 2 – Enable Envers4 and Auditing

In this step we will

  • enable Envers4,
  • add auditing to the domain class,
  • store some data and
  • analyze the modifications to the database.

To enable Envers4 in Grails 2.3.0 it is necessary to declare the dependency in BuildConfig.groovy.

// BuildConfig.groovy
   dependencies {
      // specify dependencies here under either 'build', 'compile', 'runtime', 'test' or 'provided' scopes e.g.
      compile ('org.hibernate:hibernate-envers:4.1.8.Final') {
         // Grails already includes all of the necessary dependencies
         transitive = false
      }
    }

Adding the auditing capabilities to the domain object is easily done by just using the @Audited annotation.

// Customer.groovy
package grailshibernateenvers4
import org.hibernate.envers.Audited

@Audited
class Customer {
   Long id
   String name
   String phone
   static constraints = {
       name blank: false, nullable: false
       phone blank: false, nullable: false
    }
}

Bootstrap.groovy needs a small modification withTransaction, so the data will be saved:

// Bootstrap.groovy
import grailshibernateenvers4.Customer
class BootStrap {
   def init = { servletContext ->
      // add data
      Customer.withTransaction {
         def customer = new Customer()
         customer.name = "Muller"
         customer.phone = "01234"
         assert customer.save(flush: true)
      }
   }
   ...
}

Run the grails app and check the database once again.

database2

Envers4 added 2 tables into the database, where the revision data will be stored:

  • CUSTOMER_AUD
  • REVINFO

Step 3 – Use Auditing and Versioning

Next we

  • store data,
  • modify it and
  • retrieve the revision data

from the database.

First we edit Bootstrap.groovy. What we want to do is to store the data, as already known. Then get the dataset from the database, modify the phone number and update the database.

// Bootstrap.groovy
import grailshibernateenvers4.Customer
import org.hibernate.envers.query.AuditQuery
import org.hibernate.envers.AuditReaderFactory

class BootStrap {
   def sessionFactory
   def init = { servletContext ->
      // add data
      Customer.withTransaction {
         def customer = new Customer()
         customer.name = "Muller"
         customer.phone = "01234"
         assert customer.save(flush: true)
      }
      // modify the data
      Customer.withTransaction {
         def customer = Customer.findByName("Muller")
         customer.phone = "5678"
         assert customer.save(failOnError: true, flush:true, insert:true)
      }
      // get the revision
      def auditQueryCreator = AuditReaderFactory.get(sessionFactory.currentSession).createQuery()
      AuditQuery query = auditQueryCreator.forRevisionsOfEntity(Customer.class, false, true)

      def list = query.getResultList();
      assert list.size() == 2
      println "list: ${list}"
   }
   ...
}

We use AuditReaderFactory and AuditQuery to retrieve the revisions of a particular domain class.
More information about query with Envers4 is available here.

Run the grails application.

list: [
   [Customer : 1, DefaultRevisionEntity(id = 1, revisionDate = 14.09.2013 14:06:05), ADD],
   [Customer : 1, DefaultRevisionEntity(id = 2, revisionDate = 14.09.2013 14:06:06), MOD]
]

Two revisions of the persisted domain object are stored in the database. Let us look into the database.

Revision data

In the table CUSTOMER_AUD, we see 2 datasets:

  • REV 1: the original data
  • REV 2: the modified data.

The original phone number (01234) is listed in Rev 1 and the modified phone number (5678) in Rev 2.

Summary

As we have seen, it is easily achievable to add auditing and versioning to your domain objects in Grails 2.3.0 and Hibernate4 with Envers4.

Advertisements
This entry was posted in Development, Grails and tagged , , , . Bookmark the permalink.

2 Responses to How to Use Envers4 with Hibernate4 and Grails 2.3.0

  1. Dilip shukla says:

    Very nice blog , helps me allot …Thanx..

  2. Seva says:

    Hello man, nice post. I’m using grails 2.4.5 with hibernate4:4.3.8.1 and org.hibernate:hibernate-envers:4.3.1.Final
    I’m trying run simple test and always get net exception:

    Unable to perform beforeTransactionCompletion callback
    Caused by: java.lang.NullPointerException
    at java.beans.Introspector.getBeanInfo(Introspector.java:164)

    // Test
    class TimeSheetWeekIntegrationSpec extends IntegrationSpec {
    static transactional = false

    setup:
    def policy = new WagePolicy(standardRate: 10, name: ‘test’)

    when:
    WagePolicy.withNewTransaction {
    policy.save(flush: true)
    }
    then:
    WagePolicy.count() == 1
    }

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s