Contents

Hibernate Envers with Spring

/working-with-hibernate-envers-on-spring/thumbnail.jpg
Hibernate Envers - Versioning

Hibernate envers is a Java library for maintaining versioning of our entity class. It listens to the changes made on the entity and stores it into the audit table.

Why Hibernate Envers ? 

Hibernate enver provides us an easy way to store history data for maintaining versioning. i.e Recording the changes of data from one consistent state to another. This approach of maintaining data history is known as CDC (Change Data Capture).
There are several techniques for CDC. Hibernate envers is based on the Application-level trigger.

# Database triggers

# Application-level triggers

  • Emulates database triggers at the application level. eg- Hibernate envers
    • Advantage: don’t need to mind database specific syntax for triggers
    • Disadvantage: Can’t log data changes that don’t flow through the application

# Transaction log
RDBMS uses its own way of maintaining a transaction log

  • Oracle - GoldenGate
  • SQL Server - built-in CDC
  • MySQL - 3rd party solutions, like LinkedIn DataBus

# Others

  • Debezium 
    • open source project by RedHat.
    • connectors available for Oracle, MySQL, PostgreSQL, and MongoDB
    • Also support propagation to Kafka

Enver in Spring

Setting up the dependency in pom.xml

1
2
3
4
5
<dependency>
   <groupId>org.hibernate</groupId>
   <artifactId>hibernate-envers</artifactId>
   <version>5.2.5.Final</version>
</dependency>

Hibernate Envers - Revision Table (REVINFO)

By Default Hibernate enver uses REVINFO table to log data for revision. This includes keeping track of the revision number and timestamp.
The default query for REVINFO looks like:-

1
2
3
4
5
CREATE TABLE revinfo (
 rev integer NOT NULL,
 revtstmp bigint,
 CONSTRAINT revinfo_pkey PRIMARY KEY (rev) 
)

To add more data fields to the revinfo table we can modify defults provided by hibernate envers by creating custom revision entity class.

1. Envers Configuration

(This configuration isn’t required - If we dont need custom configuration JumpTo- Setting Entity Class

  1. Create custom revision entity class adding additional fields to it.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
@Entity
@RevisionEntity(CustomRevisionListener.class)
// Custom Class adding additional fields to the envers default "reverifo" table
public class AuditEnversInfo extends DefaultRevisionEntity {

   private String source;
   private long userId;

   // Getter and Setter
}
  1. Set value to the added additional fields.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
public class CustomRevisionListener implements RevisionListener {

   // Setting value to the added addtional fields - our custom revision entity "auditEnversInfo".
   @Override
   public void newRevision(Object revisionEntity) {
       AuditEnversInfo auditEnversInfo = (AuditEnversInfo) revisionEntity;
       auditEnversInfo.setSource("In application");
       auditEnversInfo.setUserId(1L);
   }
}

2. Setting Entity class for Audit

In Original Entity Tell which entity or attribute to be audited using annotation @Audited

1
2
3
4
5
@Entity
@Audited
public class Customer {
  // ..
 }

@Audited creates Audit table for each entity. The structure looks like:

/working-with-hibernate-envers-on-spring/tables-overview.png
Envers table - Entiy, Entity Audit, RevisionInfo

As from figure, the Entity Audit Table ( customer_aud ) contains:

  • Primary key same as the original entity
  • contains all audited fields declared using @Audited annotation.
  • revision number (used with primary id column to create combined primary key)
  • revision type (type of operation performed on entity) i.e 0- Added, 1- Updated, 2- Deleted

Envers stores data into the Audit table by listening to the operations - create, update and delete.

Envers on bulk or stateless operation
If you are using Native SQL or JPA’s CriteriaUpdate/CriteriaDelete operations to manipulate database records, then no Envers will not pick up those changes. That is because Hibernate will not raise an event for those bulk or stateless operations allowing Envers to audit those changes.

3. Retrieving data from the revision table

To Read the Information we can use getRevisions() method passing our entity class.

1
2
AuditReader auditReader = AuditReaderFactory.get(em);
List<Number> revisionNumbers = auditReader.getRevisions(Entity.class, e.getId());

Lets say we have records of Employess with managed revision details i.e @Audited.

1
2
3
4
5
List<Employee> revisionsEmployees = AuditReaderFactory.get(em)
                .createQuery()
                .forRevisionsOfEntity(Asset.class, true, true)
                .add(AuditEntity.id().eq(id))
                .getResultList();

For more info on Hibernate Envers and Audit See: