Intercepting hibernate CRUD operations

This post explains how to intercept hibernate create, read, update and delete operations, so that we can add our own logic that has to be performaned before CRUD operations. The different use case where we need to intercept these hibernate operations are as follows

1) Every entity in the applications has common fields for example last created and updated date time, updated user id etc that has to be set before the actual save is called. We can centralize the code in a common place which in this case will be our custom interceptor.

2) Adding code to log the details of the CRUD operations in a log file.

Hibernate helps us in intercepting those operations by providing ‘Interceptor’ interface and its default implementation ‘EmptyInterceptor’. We can either implement the interface or extend the default implementation to add our logic.

Below is the complete code explaining how to use hibernate interceptor.

MyHibernateInterceptor


import java.io.Serializable;

import org.hibernate.EmptyInterceptor;
import org.hibernate.type.Type;

public class MyHibernateInterceptor extends EmptyInterceptor {
    @Override
    public boolean onLoad(Object entity, Serializable id, Object[] state,
            String[] propertyNames, Type[] types) {
        boolean result = super.onLoad(entity, id, state, propertyNames, types);
        System.out.println("Loading: " + entity + ":" + id);
        System.out.println("Printing properties");
        for(String propertyName : propertyNames) {
            System.out.println(propertyName);
        }
        System.out.println("Printing Types");
        for(Type type : types) {
            System.out.println(type.getName());
        }
        System.out.println("Printing state");
        for(Object obj : state) {
            System.out.println(obj);
        }
        System.out.println();
        return result;
    }
    
    @Override
    public void onDelete(Object entity, Serializable id, Object[] state,
            String[] propertyNames, Type[] types) {
        super.onDelete(entity, id, state, propertyNames, types);
        System.out.println("Deleting: " + entity + ":" + id);
        System.out.println("Printing properties");
        for(String propertyName : propertyNames) {
            System.out.println(propertyName);
        }
        System.out.println("Printing Types");
        for(Type type : types) {
            System.out.println(type.getName());
        }
        System.out.println("Printing state");
        for(Object obj : state) {
            System.out.println(obj);
        }
        System.out.println();
    }
    
    @Override
    public boolean onSave(Object entity, Serializable id, Object[] state,
            String[] propertyNames, Type[] types) {
        boolean result = super.onSave(entity, id, state, propertyNames, types);
        System.out.println("Saving: " + entity + ":" + id);
        System.out.println("Printing properties");
        for(String propertyName : propertyNames) {
            System.out.println(propertyName);
        }
        System.out.println("Printing Types");
        for(Type type : types) {
            System.out.println(type.getName());
        }
        System.out.println("Printing state");
        for(Object obj : state) {
            System.out.println(obj);
        }
        System.out.println();
        return result;
    }
    
    @Override
    public boolean onFlushDirty(Object entity, Serializable id,
            Object[] currentState, Object[] previousState,
            String[] propertyNames, Type[] types) {
        boolean result = super.onFlushDirty(entity, id, currentState, previousState,
                propertyNames, types);
        System.out.println("Updating: " + entity + ":" + id);
        System.out.println("Printing properties");
        for(String propertyName : propertyNames) {
            System.out.println(propertyName);
        }
        System.out.println("Printing Types");
        for(Type type : types) {
            System.out.println(type.getName());
        }
        System.out.println("Printing current state");
        for(Object obj : currentState) {
            System.out.println(obj);
        }
        System.out.println("Printing previous state");
        for(Object obj : previousState) {
            System.out.println(obj);
        }
        System.out.println();
        return result;
    }
}

HibernateUtil


import org.hibernate.SessionFactory;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.service.ServiceRegistry;

public class HibernateUtil 
{
    private static SessionFactory sessionFactory;
    private static ServiceRegistry serviceRegistry;
    
    public static SessionFactory createSessionFactory() 
    {
        if(sessionFactory == null) {
            try
            {
                final StandardServiceRegistry registry = new StandardServiceRegistryBuilder().configure().build();
                MyHibernateInterceptor myHibernateInterceptor = new MyHibernateInterceptor();
                sessionFactory = new MetadataSources( registry ).buildMetadata().getSessionFactoryBuilder().applyInterceptor(myHibernateInterceptor).build();
            }
            catch(Throwable excep)
            {
                throw new ExceptionInInitializerError(excep);
            }
        }
        return sessionFactory;
    }
    
    public static SessionFactory getSessionFactory() {
        return sessionFactory;
    }

    public static void shutdown() 
    {
        sessionFactory.close();
        StandardServiceRegistryBuilder.destroy(serviceRegistry);
    }
}

Main Class


import model.Person;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;

public class HibernateDemo3 {
    public static void main(String[] args) {
        SessionFactory sessionFactory = HibernateUtil.createSessionFactory();
        Session session = sessionFactory.openSession();

        Transaction tx = session.beginTransaction();
        Person person = new Person();
        person.setName("name1");
        person.setPhoneNumber(123456789);
        
        Integer id = (Integer)session.save(person);
        tx.commit();
        
        session.close();
        
        session = sessionFactory.openSession();
        person = session.load(Person.class, id);
        tx = session.beginTransaction();
        person.setName("name2");
        session.saveOrUpdate(person);
        tx.commit();
        session.close();
        
        session = sessionFactory.openSession();
        person = session.load(Person.class, id);
        tx = session.beginTransaction();
        session.delete(person);
        tx.commit();
        
        session.close();
        
        HibernateUtil.shutdown();
    }
}

Explanation

If we see the HibernateUtil code, an instance of MyHibernateInterceptor is created and added to the SessionFactory with the below code

MyHibernateInterceptor myHibernateInterceptor = new MyHibernateInterceptor();
sessionFactory = new MetadataSources( registry ).buildMetadata().getSessionFactoryBuilder().applyInterceptor(myHibernateInterceptor).build();

The Interceptor’s onFlushDirty is called when updating a modified object.

Note: The code in HibernateUtil will work for Hibernate 5 and above.

Output

Saving: model.Person@51e8e6e6:null
Printing properties
name
phoneNumber
Printing Types
string
integer
Printing state
name1
123456789

Loading: model.Person@346a361:2
Printing properties
name
phoneNumber
Printing Types
string
integer
Printing state
name1
123456789

Updating: model.Person@346a361:2
Printing properties
name
phoneNumber
Printing Types
string
integer
Printing current state
name2
123456789
Printing previous state
name1
123456789

Loading: model.Person@6256ac4f:2
Printing properties
name
phoneNumber
Printing Types
string
integer
Printing state
name2
123456789

Deleting: model.Person@6256ac4f:2
Printing properties
name
phoneNumber
Printing Types
string
integer
Printing state
name2
123456789

Leave a Reply