Sunday, 13 October 2013

Audit Logging using Hibernate Interceptor in JPA 2.0

If you are using JPA 2.0 with Hibernate and you want to do audit logging from middle-ware itself, I believe you landed up on the exact place where you should be. You can try audit logging in your local environment by following this post.

Required JPA/Hibernate Maven Dependencies


JPA Configuration in Spring Application Context File

For JPA/Hibernate, you need to configure entity manager, transaction, data source and JPA vendor in your ‘applicationContext.xml’ file.

<bean id="dataSource"
        p:url="jdbc:oracle:thin:@ "
         p:username="nvuser" p:password="nvpass" />

<bean id="entityManagerFactory"
         p:dataSource-ref="dataSource" p:jpaVendorAdapter-ref="jpaAdapter">
         <property name="loadTimeWeaver">
            <bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver"/>

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"
                 p:entityManagerFactory-ref="entityManagerFactory" />

<bean id="jpaAdapter"
                 p:database="ORACLE" p:showSql="true" />

Persistence.xml Configuration

To enable the hibernate audit logging you need to configure a property called ‘hibernate.ejb.interceptor’ in persistence.xml file and the value of this property should be the class that extends the Hibernate ‘EmptyInterceptor’.

<?xml version=”1.0” encoding=”UTF-8”?>
<persistence version="2.0"
         xmlns="" xmlns:xsi=""
         <persistence-unit name=" MyPersistentUnit ">
                 //JPA Entity classes are configured here
                 <class>com.mycom.entities.User </class>           


                 <property name="hibernate.ejb.interceptor"
                          value="" />

Audit Logging Interceptor Implementation

Hibernate provides an interceptor called ‘EmptyInterceptor’ with following methods.
  • onSave() – This method is called when an entity is saved, but the entity is not saved into database yet.
  • onFlushDirty() – This method is called when an entity is updated, the entity is not update into database yet.
  • onDelete () – This method is called when an entity is deleted , the entity is not deleted into database yet.
  • preFlush() – This method is called before the saved, updated or deleted entities are not committed to the database.
  • postFlush() – This method is called after the saved, updated or deleted entities are committed to database. This is called after preFlush() method.

To perform audit logging follow below steps:
  • Create data base table to capture audit data:
          Audit [ Row_ID, Old_Value, New_Value, Column_Changed, Table_Changed,
                    Transaction_Id, User_ID]
  • Create JPA entity say ‘AuditTrail’ for above table
  • Create class ‘AuditLogInterceptor’ by extending Hibernate ‘EmptyInterceptor’.
  • Override interceptor methods in ‘AuditLogInterceptor’
  • Capture required information into AuditTrail entity and save entity

public class AuditLogInterceptor extends EmptyInterceptor {  
  private List<AuditTrail> auditTrailList = new ArrayList<AuditTrail>();    
   . . . . . .
   // First Parameter: The entity which is being updated     
   // Second Parameter: The primary key of updated row 
   // Third Parameter:  Current states of updated entity    
   // Fourth Parameter: Previous states of updated entity  
   // Fifth Parameter: The variable names of updated entity
   // Sixth Parameter: The type of variables of updated entity
   public boolean onFlushDirty(Object entity, Serializable id,
            Object[] currentState, Object[] previousState,
            String[] propertyNames, Type[] types) {                     
           // Get the table name from entity
           Class<?> entityClass = entity.getClass();
            Table tableAnnotation = entityClass.getAnnotation(Table.class);            
           for (int i = 0; i < currentState.length; i++) {              
                // Track changes only for the states which are String or Number types           
                 if(!(previousState[i] instanceof String
                     || previousState[i] instanceof Long
                     || previousState[i] instanceof BigDecimal
                      || previousState[i] instanceof Integer
                     || previousState[i] instanceof Timestamp))
                // Check whether column value is updated
                 if(previousState[i] != null && currentState[i] != null
                   &&  !(previousState[i].equals(currentState[i]))){
                  AuditTrail auditTrail = new AuditTrail();               
                 // Set updated table name
                 // Set updated column name
                 Column col = null;
                 try {                                                  
                        String filedName = propertyNames[i];
                        Character firstChar =  filedName.charAt(0);
                        firstChar = Character.toUpperCase(firstChar);
                        String filedNameSubStr = filedName.substring(1);
                        String methodName = "get"+ firstChar+filedNameSubStr;                       
                        Class[] parameterTypes = new Class[]{};                           
                        Method ueMethod = entityClass.getDeclaredMethod(methodName, parameterTypes);
                        col = ueMethod.getAnnotation(Column.class);                         
                    } catch (Exception e) {
                   if(col != null){
                // Set old value
                if(previousState[i] != null){
                          auditTrail.setOldValue(previousState[i] == null  ? null :
                // Set new value
                if(currentState[i] != null){
                          auditTrail.setNewValue(currentState[i] == null ? null:
                // Set database operation
                // Set row primary key value
        return false;
    public void postFlush(Iterator iterator) throws CallbackException {            
           // Set unique transaction id in all AuditTrail Entities
           String transId = UUID.randomUUID().toString();     
           for (AuditTrail auditTrailauditTrailList) {
          // Save ‘AuditTrail’ entity list into database using AuditDAO 

Audit logging Sequence Flow

Issue Faced during Audit Logging Implementation

I faced an interesting issue during audit logging implementation in my application. Let me share that issue detail.

I had ‘AbstractDAO’ and 'AuditDAO' class extends this ‘AbstractDAO’. 'AuditDAO' class is configured as ‘@Repository’. AbstractDAO’ has entity manager property which is initialized by Spring Container. I wanted to auto wire ‘AuditDAO’ in ‘AuditLogInterceptor’ to save ‘AuditTrail’ entity. But, this couldn’t happen. The reason was ‘AuditLogInterceptor’ was not defined as spring resource (like @Component or @Resources or @Repository). Hence's its property can’t be auto wired. Since, this interceptor is initialized by hibernate, I couldn't configure this as spring resource. Finally, I had to take the help of StackOverflow and got the idea to solve this problem. You can refer this issue and solution from here.