Spring Data JPA, Spring Security and Envers are libraries that I personally enjoy working with (and I tend to think they are considered best-of-breed in their respective category). Anyway, I wanted to implement what I consider a simple use-case: entities have to be Envers-audited but the revision has to contain the identity of the user that initiated the action. Although it seem simple, I had some challenges to overcome to achieve this. This article lists them and provide a possible solution.

Software architecture

I used Spring MVC as the web framework, configured to use Spring Security. In order to ease my development, Spring Security was configured with the login/password to recognize. It's possible to connect it to a more adequate backend easily. In the same spirit, I use a datasource wrapper around a driver manager connection on a H2 memory database. This is simply changed by Spring configuration.A typical flow is handled by my Spring MVC Controller, and passed to the injected service, which manages the Spring Data JPA repository (the component that accesses the database).

Facts

Here are the facts that have proven to be a hindrance (read obstacles to overcome) during this development:

Spring Data JPA uses (wait for it) JPA 2. Thus, you have to parameterize your Spring configuration with LocalContainerEntityManagerFactoryBean, then configure the EntityManager factory bean with the implementation to use (Hibernate in our case). Some passed parameters are portable across different JPA implementations, other address Envers specifically and are thus completely non-portable.

Spring Data JPA does provide some auditing capabilities in the form of the Auditable interface. Auditables have createdBy, createdDate, modifiedBy and modifiedDateproperties. Unfortunately, the library doesn't store previous state of the entities: we do have to use Envers that provide this feature

After having fought like mad, I realized integrating Envers in Spring Data JPA was no small potatoes and stumbled upon the Spring Data Envers module, which does exactly that job. I did founf no available Maven artifacts in any repository, so I cloned the Git repo and built the current version (0.1.0.BUILD-SNAPSHOT then). It provided me with the opportunity to give a very modest contribution to the project.

On the bright side, and despite all examples I googled, the latest versions of Hibernate not only are shipped with Envers, but there's no configuration needed to register Envers listeners, thanks to a smart service provider use. You only need to provide the JAR on the classpath, and the JAR itself takes care of registration. This makes Envers integration much simpler.

Bad News

... Aren't you? The aforementioned solution works perfectly when writing to the database. The rub comes from trying to get the information once it's there, because the Spring Data API doesn't allow that (yet?). So, there are basically three options (assuming you care about retrieval: this kind of information may not be for the user to see, and you can always connect to the database to query when -if - you need to access the data):

Create code to retrieve the data. Obviously, you cannot use Spring Data JPA (your entity is missing the userfield, it's "created" at runtime). The best way IMHO would be to create a listener that get data on read queries for the audited entity and returns an enhanced entity. Performance as well as design considerations aren't in favor of this one.

Hack the API, using reflection that make it dependent on internals of Spring Data. This is as bad an option as the one before, but I did it for educational purposes:

Last but not least, you can contribute to the code of Spring Data. Granted, you'll need knowledge of the API and the internals, skills and time, but it's worth it :-)

Conclusion

Integrating heterogeneous libraries is hardly a walkover. In the case of Spring Data JPA and Envers, it's as easy as pie thanks to the Spring Data Envers library. However, if you need to make your audit data accessible, you need to integrate further.