A lot of business applications require an audit log that documents all changes that were performed on the managed data. There are lots of different options to implement such a log. One of them is Hibernate Envers. It just takes a few annotations to document all changes in the audit tables, and Envers also provides a powerful API to extract information from your audit log.
In this first post of the series, I will show you how to add Hibernate Envers to your project, activate auditing for an entity and retrieve different information from your log.
It’s pretty easy to add Hibernate Envers to an existing application. You just have to add the hibernate-envers.jar file to the classpath. If you’re using maven, you can do that with the following maven dependency.
The next thing you need to do is to setup the audit tables. Hibernate can do that itself, if you use the automatic schema generation feature. But I don’t recommend that approach. You can use that feature to create your database script, but you shouldn’t deploy it to production without reviewing and improving it.
So, here are the tables you need to create in your database setup or migration script:
Get a free cheat sheet with everything you have to remember!I've prepared a free cheat sheet for you with the most important information and code snippets of this post. As always, you can download it for free from the Thoughts on Java Library.
This table stores the revision information. By default, Hibernate persists only the revision number as an integer and the creation timestamp as a long.
I will show you how to add more information to this table, like the user who created the revision, in one of the following blog posts.
An audit table for each entity
You also need to create an audit table for each entity you want to audit. By default, Hibernate adds the “_AUD” suffix to the table name of the audited entity. You can define a different table name with the @AuditTable annotation or by configuring a different prefix or suffix in the configuration.
Each audit table contains the primary key of the original entity, all audited fields, the revision number and the revision type. The revision number has to match a record in the revision table and is used together with the id column to create a combined primary key. The revision type persists the type of operation that was performed on the entity in the given revision. Envers uses the integer values 0, 1 and 2 to store that an entity was added, updated or deleted.
The following code snippet shows an example of an audit table. It stores the audit information for the Author entity and tracks all changes to the firstname and lastname attributes.
That’s all you need to do to add Hibernate Envers to your application. You can now tell Hibernate which entities you want to audit.
Audit an entity
If you want to audit all changes of an entity, you have to annotate it with @Audited. That tells Hibernate Envers to audit the values of all attributes for all create, update and delete operations. You can, of course, apply additional annotations and configuration to adapt the audit to your needs. I’ll explain that in more detail in one of the following posts.
After you annotated your entities with @Audited, Hibernate will create a new revision for each transaction and document all changes in the audit tables. The following code snippets show basic persist and update operations and the SQL statements Hibernate executes. As you can see, you don’t need to adapt your business code in any way.
Retrieve basic audit information
Hibernate Envers provides an extensive Query API which you can use to extract the required information from your audit log. In this post, I just show you how to retrieve all revisions of an entity and how to retrieve the revision that was active at a certain point in time. These are only 2 basic use cases, and you can do a lot more with the Query API. I will show you that in more detail in a future blog post.
Get all revisions of an entity
The first thing you need to do to access your audit information is to create an AuditReader via the AuditReaderFactory. You can see an example of it in the first line of the following code snippet. I call the get method of the AuditReaderFactor with the current instance of the EntityManager.
The getRevisions method of the AuditReader returns all revision numbers of a given entity. You can use these numbers to get an entity with all the attribute values it had at a given revision. I do that in the 5th line of the code snippet. I iterate through the List of revision numbers and call the find method for each of them to get the Book entity that was active at the given revision.
As you can see in the log messages, Hibernate performs an SQL query to get al revision number for the given entity. The call of the find method triggers another SQL query that returns the record from the audit table which was activate for the given revision number.
Get active revision at a given date
If you just want to get an entity that was active at a given time, you can call the find method of the AuditReader and provide a java.util.Date instead of a revision number. You can see an example of it in the following code snippet.
Hibernate Envers will then perform an SQL query to get the revision number that was active at the given time and perform an additional query to select the record from the audit table.
Hibernate Envers provides a powerful and easy to use API to write and read audit information. You just need to add the hibernate-envers.jar file to the classpath of your application and annotate your entities with @Audited. Hibernate will then create a new revision for each transaction and create a new record in the audit table for each create, update or delete operation performed on an audited entity.
This post provided only a short introduction to Hibernate Envers. In the following blog posts, I will show you more of the Query API and how you can customize your audit log.