Audit Columns with Hibernate 4

This post is referencing Hibernate 4.2.7, Spring 3.2.4 and Spring Security 3.1.4

Hooking up audit columns with Hibernate 4 can be a bit of a head scratcher as the Hibernate team has changed the API some. It does not help much that Google seems to still prefer the 3.x docs over the 4.x ones.

This was relatively easy with NHibernate (which I have used for a number of years now) and a similar setup was found in Hibernate 3.x

The goal is deceptively simple, get the current authenticated user and set the audit fields on a base entity to the username and the current timestamp.

This can be done in a couple different ways mainly with Interceptors and Event Listeners. Here I have chosen to use Hibernate’s Event Listeners and Spring Security with a simplistic security model.

The Base Entity

The base entity has four fields we are interested in. We are also using Joda-Time and Jadira for the DateTimes.

baseEntity

The Listeners

We will have two listeners, on for inserts and one for updates.

The PreInsertListener

preInsertListener

The PreUpdateListener

preUpdateListener

You will notice in the Listeners we first check to make sure the entity involved with the event is the entity with our fields, in this case a DomainEntity.

We then get a date in UTC. I am big fan of setting dates in UTC especially if you are moving to cloud based implementations where the local TimeZone could be anything. I have had all kinds of problems getting the database (in this case MySql) to respect the datetime given, in the code it would be in UTC but in MySql it would be local. The only why I got this to work was by setting the TimeZone at the JVM level or as shown setting the default TimeZone before creating a new DateTime.

We then need to get the metadata and properties out of the entity, as we have to set BOTH the meta data properties and the entity properties.

We also use Spring’s Security to get the authentication out of the SecurityContext.

The Integrator

integrator

The integrator is how we can tell Hibernate about our Listeners.

This Integrator and the Listeners are NOT Spring enabled. If you need to “Springify” them check out this post on Stackoverflow.

The missing piece of magic to make this all work, and the piece that stumped for the longest time is the Integrator Service Loader text file.

Integrator Service Loader

Create a text file named at /META-INF/services named org.hibernate.integrator.spi.Integrator.

Inside the text file, add the fully qualified name (including package) of the integrator. For example:

com.mycoolstuff.domain.data.impl.HibernateIntegrator    #Hibernate Integrator to hookup listeners

You can find more info on this in the Hibernate Docs.

Set the Authentication Context

Finally you need to set the Authentication in the SecurityContext.

In this case this is a pretty simplistic example where we are creating an API that must have an Authentication value set in the request header.

We need to extract the header and set the context, if the header is missing we need to return a 401 Unauthorized status.

We do this with a HandlerInterceptorAdapter

inteceptor

We are setting the Authentication to an ApiAuthentication, this class just implements Authentication and takes a username, folded methods have no implementation.

ApiAuthentication

authentication

Finally we need to tell Spring about the Interceptor.

Dispatcher-Servlet

dispatcher

And that should be it.

Now when requests come into the /api/** route, the interceptor will pick up the authentication header and set it in the SecurityContext.

Then when an entity is persisted, the Listeners will extract the username from the SecurityContext and set the username and timestamps on the entity.

TrackBack

TrackBack URL for this entry:
https://www.typepad.com/services/trackback/6a0133ecbab7ab970b019b0287615b970b

Listed below are links to weblogs that reference Audit Columns with Hibernate 4:

Comments

You can follow this conversation by subscribing to the comment feed for this post.

The comments to this entry are closed.