In the previous posts of this series, I showed you how to add Hibernate Envers to your project to write an audit log and how to use its query API to search in your audit log. These posts provide you an introduction to Hibernate Envers and allow you to write a basic audit log. But the default audit log information are not sufficient for a lot of applications.
Hibernate Envers audits what had happened but not who did it. The default revision entity doesn’t store any information of the user who performed the operations. If you also want to store user information, like the username or IP address, you have to use a custom entity to store the revision.
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.
Create a custom revision entity
You just need to implement 2 classes to create and register a custom revision entity: the revision entity and a RevisionListener. Let’s have a look at the revision entity first.
The easiest way to implement your own revision entity is to extend the as I do in the following code snippet. If you don’t want to do that, your entity needs to have at least 2 attributes:
- a revision number of type int/Integer or long/Long annotated with @RevisionNumber and
- a revision timestamp of type long/Long or java.util.Date annotated with @RevisionTimestamp.
In this example, I extend the DefaultRevisionEntity because I just want to store an additional userName attribute for each revision.
As you can see in the code snippet, you also need to annotate your revision entity with @RevisionEntity and provide an implementation of the RevisionListener interface as a parameter. The implementation of the RevisionListener interface is the second class you need to implement. It tells Hibernate Envers how to set the attributes of the revision entity. You can see an example of it in the following code snippet. You just need to implement the newRevision(Object revisionEntity) method which gets the newly instantiated revision entity as a parameter. The only thing you have to do is to set the additional attributes. In this example, I just need to set the userName attribute. The code snippet doesn’t show the code of the getUserName() method because it’s specific to your technology stack. Spring and all Java EE application server provide a way to get the current user. Please check your documentation to learn more about it.
That’s all you have to do to create and register your custom revision entity. Hibernate will now persist it with all its attributes and you can use them to retrieve data from your audit log.
Use revision data in queries
Hibernate Envers provides a powerful query API that allows you to look at your log from a horizontal or vertical perspective. You can also define a set of expressions to retrieve the revisions or entities you’re looking for. I explained that in more detail in the previous post of this series.
When you create your AuditQuery, you can use the attributes of the revision entity in the same way as any attribute of an audited entity. The following code snippet shows an example in which I select the numbers of all revisions in which a user with userName “User 1” created, updated or deleted a Book entity.
Hibernate Envers’ default settings provide an easy way to create an audit log and to retrieve information from it. Unfortunately, it doesn’t store any information about the user who performed the audited operations.
From a framework point of view, this is the right approach. User authentication isn’t part of Hibernate, and it doesn’t have a generic way to retrieve this information. It can, therefore, also not depend on them.
But it also creates additional work for a lot of applications. In most cases, it’s not enough to document when someone changed which data. You also need to store who performed these operations.
As I showed you in today’s post, you can easily do that with a custom revision entity. You just need to provide your own entity and a RevisionListener to Hibernate Envers. Envers will then use it instead of the default entity, and you can store all the revision information you need.