Tuesday, February 27, 2018

Hibernate Envers

Please follow Rest API Server to see the big picture and GitHub repo details.

Hibernate Envers helps in creating an audit trail of the records in the database. Hibernate Envers audits all the attribute changes in an audit table. To enable the support for Hibernate Envers, add the dependency

    compile('org.hibernate:hibernate-envers')

Add the appropriate version. If you are using Spring boot dependency management then the version will be automatically taken care by Spring boot.


First, define a table Audit where Envers can start writing the changes. The liquibase XML of the schema will be on the following line.

<createSequence sequenceName="audit_seq" cycle="true"
incrementBy="1" minValue="1" schemaName="public" startValue="1" />
<createTable tableName="audit" remarks="Hibernate envers audit table">
<column name="id" type="bigint" defaultValueComputed="nextval('audit_seq')">
<constraints nullable="false" unique="true" primaryKey="true" />
</column>
<column name="timestamp" type="bigint">
<constraints nullable="false" />
</column>
<column name="email" type="varchar(100)"/>
</createTable>

The corresponding entity definition is

@Entity(name = "audit")
@RevisionEntity(AuditListener.class)
@Data
public class Audit implements Serializable{
private static final long serialVersionUID = 1L;

@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "audit_sequence")
@SequenceGenerator(name = "audit_sequence", sequenceName = "audit_seq", allocationSize = 50)
@Column(name = "id", updatable = false, nullable = false)
@RevisionNumber
private Long id;

@RevisionTimestamp
private Long timestamp;

@Column(name = "email")
private String email;
}

And the Listener class

public class AuditListener implements RevisionListener {

@Override
public void newRevision(Object revisionEntity) {
Audit audit = (Audit) revisionEntity;
String email = SecurityContextHolder.getContext().getAuthentication().getName();
audit.setEmail(email);
}
}

Now whenever any entity insert, update and delete happens, an entry is done in the Audit table. One can extend Audit entity to add more data. In this example, I am inserting the email of the user which is also user id.

Also, an entry is done for each individual entity in their respective audit table. For auditing individual entity, in the class definition of the entity, mark it as audited so that Hibernate Envers can track it.

@Entity
@Table(name = "users")
@Audited
@AuditTable(value = "zzz_users_aud")
@Data
public class User implements Serializable {

@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "users_sequence")
@SequenceGenerator(name="users_sequence", sequenceName = "users_seq", allocationSize=50)
@Column(name = "id", updatable = false, nullable = false)
private Long id;

@Column(name="email")

private String email;

        @Column(name="password")
@NotAudited
private String password;

Putting Audited in the class definition enables the auditing of each of field by default. If you want to exclude any column, then mark it with @NotAudited. Alternately, you can mark only the columns with @Audited

The audit table definition as per liquibase XML will be

<createTable tableName="zzz_users_aud" remarks="User Audit table">
       <column name="rev" type="bigint">
 <constraints nullable="false" foreignKeyName="fk_users_audit" references="audit(id)"/>   
      </column>
     <column name="revtype" type="smallint" />
     <column name="id" type="bigint" />
     <column name="first_name" type="varchar(50)"/>
     <column name="last_name" type="varchar(50)"/>
     <column name="email" type="varchar(100)"/> 
    <column name="role" type="varchar(10)"/>
</createTable>

Hibernate Envers cannot track bulk operations like bulk delete and bulk updates. Also, it cannot track changes which happen to the database outside of the Hibernate ecosystem.

Now if you create a new user, then Audit table will have a record

id               | 1
timestamp | 1519746212197
email         | admin@admin.admin

Where the email is of the user who has created the record

And the zzz_users_aud table will contain a record

rev               | 1
revtype        | 0
id                 | 1
first_name   | Ekagra
last_name    | Bhatt
email           | ekagara.bhatt@eb
role              | ADMIN

revtype tells about the type of change.

0 - Add
1 - Modify
2- Delete

Hibernate Envers provides an AuditReader class which can help in fetching the different revisions.

No comments:

Post a Comment