Hibernate Envers for Auditing

Necmeddin Tapan
turkcell
Published in
5 min readDec 3, 2021

Since data is too important nowadays, to keep track of data changes in the applications became a highly preferred requirement. There are options to achieve this goal and Hibernate Envers is one of these solutions. With Hibernate Envers, it is easy to document and audit all changes on the persisted Entities. History of an entity with all changes can be found in the audit tables. Also, some extra information like “who did change”, can be added to the audit tables.

1- Basic Usage

It is easy to add Hibernate Envers to the application, just add hibernate-envers jar to your classpath:

Next step is to add @Audited annotation to your Entity, which you want to audit. With this annotation, Envers will audit all changes that results from insert, update and delete operations. @Audited annotation can be added to both Entity (for entire table) and Columns( for specific attribute).

@AuditTable is an optional annotation, that defines the name of the audit table in the database. In default case, Envers creates audit table with the name of “tableName_AUD”. Audit table suffix (for all audit tables) and other configurations can be changed in application properties file.

In case of not to audit a field of an audited Entity, @NotAudited annotation should be used. As an example, password changes are not important for auditing. With @NotAudited annotation, changes of “password” attribute will be ignored by Envers.

Note that, relation of “issues” is also @NotAudited for User entity. If it is wanted to be audited, Issue entity must be audited with @Audited annotation.

Now, it is time to create audit tables in database. If you use “ddl-auto: update” in hibernate configuration, tables will be created automatically.

If you want to create tables manually, first table must be “REVINFO”. It keeps “rev” id for audited tables as foreign key and “revtstmp” indicates change time as long value.

Audit tables for all your Entities (if @Audited), like “USERS_AUDIT” must be created. While creating entity_audit tables, you should be aware of the audited fields, not to add @NotAudited fields. “rev” and “revtype” fields must be added to audited tables. “revtype” in audit table is for change type ( 0-> insert, 1->update, 2->delete).

Note that, “sequence” also has to be created in database for “rev” id to incerement.

That is all should be done, Envers is ready to run. After successful build, you will see below tables in database.

As seen above, USERS_AUDIT table has all audited fields (not password), also “rev” and “revtype” fields. From now on, all changes on User instances (USERS table) will be audited in this table.

Note that, if you use native query in order to change User Entity, it can’t be catched by Hibernate Envers, so these changes can not be audited.

2- Extend With New Fields

First part is the basic usage of Envers, in this part new features will be added. Revision table can be extended with new information, such as the “username” or “ip address” of the user who has done any change on the audited entity.

In order to add new fields, custom RevisionEntity class that extends DefaultRevisionEntity must be written. The new class should contain the fields that you want to audit about changes like “modifierUser” who modified the audited Entity instance.

Note that, MyRevisionEntity will be created as a different table in database. If there is “revinfo” and “audit” tables before MyRevisionEntity, it may cause runtime error. To avoid this, you may need to drop revinfo and audit tables or change MyRevisionEntity table name to “revinfo”.

As seen in MyRevisionEntity class, @RevisionEntity annotation needs a custom RevisionListener class. It implements RevisionListener and must override “newVersion” method. This method should set newly added field (modifierUser) in RevisionEntity.

withModifiedFlag = true” property can be added to @Audited annotation. It helps to find which fields has been changed. This option will be more clear when we write queries for audit logs.

After changes, final tables should be as below. As seen, there is “MY_REVISION_ENTITY” table instead of “revinfo” and has extra field named “MODIFIER_USER”. Also, there are new fields in USERS_AUDIT table, those have “_MOD” suffix. It is about “withModifiedFlag = true” option and indicates whether the field has been changed or not.

3- Query on Audit Logs

In previous parts, Envers captures all changes about the Entity and create audit logs. In order to search among these audit logs, AuditReader interface should be used. It can be obtained from EntityManager or Session by AuditReaderFactory.

First sample query is to get all history (revisions) of a specific user with “.eq” filter. Filtered property (in this example = “id”) should be one of the audited fields in the Entity class. In “forRevisionsOfEntity(User.class, true, true)” User.class defines which Entity’s logs will be gotten. Other parameters are “selectEntitiesOnly” and “selectDeletedEntities”.

In first example “selectEntitiesOnly=true” choosen, and as a result, returning resultList type is User.class. What if we want to get all history of a specific user with revision info. For this, “selectEntitiesOnly=falseshould be used. As seen below, returning resultList type has changed to three object arrays.

  • First one is the entity that has been changed.
  • Second one is MyRevisionEntity that keeps the “who changed”,”when changed” and “rev” information. If custom RevisionEntity class was not used, DefaultRevisionEntity should be return type.
  • Third one is RevisionType, enumeration of the operation type : ADD(insert) , MOD(update), DEL(delete).

In order to get history of Users but just only “email” attribute has been changed, “.hasChanged()” should be applied to query. To be able to use this query, “withModifiedFlag=true” must be added in @Audited annotation. Both “.eq” and “.hasChanged()” can be used at same time to make more specific queries.

It is time to use “modifierUser” attribute that has been added to custom RevisionEntity. To get all history of Users which has been modified by a specific user, revisionProperty should be used like in the below example.

Note that, all queries options and samples can be found in the Envers Documentation

Hope, it will be helpful…

--

--