16 - Hibernate Interceptors and Events

16.1 Overview

Hibernate is all about Entity persistence and quite often we would want to intercept the request or perform some tasks when state of an object changes. With the help of interceptors , we get an opportunity to inspect the state of an object and if needed we can change the state as well.To support this Hibernate does support the concept of interceptor and provides a Interface and a implementation of Interface. Developers can either directly implement the Interface or may extend the implementation.

Similarly Hibernate comes with event and listener model which can be used to track the events .

16.2 Interceptors

16.2.1  Interceptor Interface API

 Depending on requirement we can have a single interceptor instance for Session factory or we may create a new for a session  

We can directly implement org.hibernate.Interceptor available in Hibernate. There are several methods available but most commonly used are –

  • onSave()- this method is called before on object is getting saved and we can modify the state of an instance. Modified state will be persisted.
  • onDelete()-is called before on object is getting deleted.
  • onLoad()is called before object initializes and when this method is called passed entity will be uninitialized.  
  • findDirty()- is called when flush method of session is called.
  • preFlush()- is called before flush method is called (before database is updated).
  • postFlush() – is called after flush method is completed (after database is updated)

To activate interceptor for a specific entities , you need to check the type of instance using instanceof operator else interceptor will work for all the entities.

16.2.2  EmptyInterceptor

Hibernate comes with an empty implementation of Interceptor interface under package org.hibernate with name EmptyInterceptor. It is always advisable to extend EmptyInterceptor instead of implementing Interceptor interface as we would just need to implement the required method and not the entire interface.

16.2.3  Configure Interceptor

Interceptor can be configured at a session level and at a session factory level.

  1. At a Session Level -  Pass an instance of Interceptor while opening a session.

      In hibernate 3 you can use SessionFactory.openSession( Interceptor) whereas in hibernate 4 SessionFactory.withOptions() .interceptor(Interceptor()).openSession();

      This way we can get a control at a session level and can enable interceptor for a desired sessions only.

  1. At a Session Factory Level – Set an interceptor in a Configuration Object. Doing this , interceptor will be enabled for all the session for the session factory.                                                                                                               Like  Configuration.setInterceptor(Interceptor)

16.2.4  Example

Let's write an interceptor to log the audit statements on console. 

a. Hibernate.cfg.xml

<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
    "-//Hibernate/Hibernate Configuration DTD//EN"
    "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

<hibernate-configuration>

    <session-factory>

        <property name="hibernate.connection.url">
            jdbc:mysql://localhost:3306/tutorial
        </property>
        <property name="hibernate.connection.username">
            root
        </property>
        <property name="hibernate.connection.password">
            password
        </property>
        <property name="dialect">org.hibernate.dialect.MySQLDialect</property>

        <property name="show_sql">true</property>

        <property name="hibernate.connection.driver_class">
            com.mysql.jdbc.Driver
        </property>
        <mapping resource="book.hbm.xml" />
    </session-factory>

</hibernate-configuration>

b. Book.java

package com.tutorial.hibernate;

public class Book {

   private int isbn;
    private String name;
    public int getIsbn() {
        return isbn;
    }
    public void setIsbn(int isbn) {
        this.isbn = isbn;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public String toString() {
        return "Book [isbn=" + isbn + ", name=" + name + "]";
    }    
}

c. Book.hbm.xml

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC 
 "-//Hibernate/Hibernate Mapping DTD//EN"
 "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<hibernate-mapping>
 
    <class name="com.tutorial.hibernate.Book" table="Book">
        <id name="isbn" type="int" column="book_id">
            <generator class="native" />
        </id>

           <property name="name" column="name" type="string" />

    
    </class>
</hibernate-mapping>

d. ConsoleLogInterceptor –  ConsoleLogInterceptor is implementation of EmptyInterceptor and prints the object state. Also refer highlighted section to get an idea how to update the value. We are updating the name of the book

package com.tutorial.hibernate;

import java.io.Serializable;
import java.util.Iterator;

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

public class ConsoleLogInterceptor extends EmptyInterceptor{

      private static final long serialVersionUID = 1L;

      @Override
      public void onDelete(Object entity, Serializable id, Object[] state,
             String[] propertyNames, Type[] types) {
             System.out.println("onDelete Method is getting called");
             System.out.println("==== DETAILS OF ENTITY ARE ====");
             if(entity instanceof Book)
             {
                System.out.println("Id of an Entity is :" + id);
                System.out.println("Property Names ");
            
                for(int i=0;i<propertyNames.length;i++)
                {
                System.out.println(propertyNames[i] );
                }

                Book book = (Book) entity;            
                System.out.println("BOOK STATE is ");
                System.out.println(book);
        }
      }

     @Override
     public boolean onLoad(Object entity, Serializable id, Object[] state,
                String[] propertyNames, Type[] types) {
            System.out.println("onLoad Method is getting called");

            System.out.println("==== DETAILS OF ENTITY ARE ====");
            if(entity instanceof Book)
            {
               System.out.println("Id of an Entity is :" + id);
               System.out.println("Property Names ");

               for(int i=0;i<propertyNames.length;i++)
               {
                  System.out.println(propertyNames[i] );
               }
 
               Book book = (Book) entity;

              System.out.println("BOOK STATE is ");
              System.out.println(book);
        }
        return true;
     }

     @Override
     public boolean onSave(Object entity, Serializable id, Object[] state,
            String[] propertyNames, Type[] types) {
        System.out.println("onsave Method is getting called");
        System.out.println("==== DETAILS OF ENTITY ARE ====");
        if(entity instanceof Book)
        {
            System.out.println("Id of an Entity is :" + id);
            System.out.println("Property Names ");

            for(int i=0;i<propertyNames.length;i++)
            {
                System.out.println(propertyNames[i] );

                if("name".equals(propertyNames[i]))
                {
                    state[i]= "Hibernate Tutorial Updated";
                }
            }

            Book book = (Book)entity;
            System.out.println("BOOK STATE is ");
            System.out.println(book);
        }
        return true;
    }    
}

e. Create Book Table 

CREATE TABLE 'book' (
  'book_id' int(11) NOT NULL AUTO_INCREMENT,
  'name' varchar(255) DEFAULT NULL,
  PRIMARY KEY ('book_id')
);

f. Test Program

import org.hibernate.Session;
import org.hibernate.SessionBuilder;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;

import com.tutorial.hibernate.Book;
import com.tutorial.hibernate.ConsoleLogInterceptor;

public class Test {

       private static SessionFactory factory;       
       public static void main(String args[])
       {    
          Configuration cfg = new Configuration().configure();
          factory = cfg.buildSessionFactory();

          SessionBuilder builder = factory.withOptions().interceptor(new ConsoleLogInterceptor());
          Session session =     builder.openSession();

          Transaction tx = session.beginTransaction();        
          Book book = new Book();
          book.setName("Hibernate tutorial");

          int id = (Integer)session.save(book);

          session.delete(book);
          tx.commit();
          session.close();
          factory.close();

      }
}

g. Run Test Program – You can refer the logs on console. Also highlighted section confirms that name of the book has been updated. 

 

              

16.3 Event System

Hibernate comes with an event system (a concept of listeners and event ) l and we can configure the events we want to track in the form of listeners. Most commonly used events are-

  1. Pre – Select:  : these types of event are fired before executing any select statement.
  2. Post – Select: : these types of event are fired after executing any select statement.
  3. Pre – Insert: : these types of event are fired before execution of insert statement.
  4. Post – Insert: : these types of event are fired after execution of  an insert statement.
  5. Pre – Update: : these types of event are fired before execution of  update statements.
  6. Post – Update: : these types of event are fired after execution of any update statement.
  7. Pre – Delete: these types of event are fired  before execution of  any delete statement.
  8. Post – Delete: these types of event are fired after execution of any delete statement.

16.3.1 Implementing Listeners

Depending on the type of event we would need to implement the Listener interface available in org.hibernate.event package. Complete list of Event listeners can be seen at https://docs.jboss.org/hibernate/orm/3.5/api/org/hibernate/event/package-summary.html

Event is getting passed in the listener and we can get the entity by calling getEntity() method of listener.

By default listener gets activated on all entities so we need to filter the entities using instaneof we are interested in.

package com.tutorial.hibernate;

import org.hibernate.event.spi.PostUpdateEvent;
import org.hibernate.event.spi.PostUpdateEventListener;
import org.hibernate.event.spi.PreUpdateEvent;
import org.hibernate.event.spi.PreUpdateEventListener;
import org.hibernate.persister.entity.EntityPersister;

public class UpdateBookEventListener implements PostUpdateEventListener, PreUpdateEventListener{

       @Override
       public boolean onPreUpdate(PreUpdateEvent preUpdateEvent) {

           System.out.println("Pre Update Event ");
           if(preUpdateEvent.getEntity() instanceof Book)
           {
                Book book = (Book)preUpdateEvent.getEntity();
                System.out.println(book.getName());
        }
        return true;
    }

    @Override
    public void onPostUpdate(PostUpdateEvent postUpdateEvent) {
        System.out.println("Post Update Event ");
        if(postUpdateEvent.getEntity() instanceof Book)
        {
            Book book = (Book)postUpdateEvent.getEntity();            
            System.out.println(book.getName());
        }        
    }

    @Override
    public boolean requiresPostCommitHanding(EntityPersister arg0) {
        
        return false;
    }    
}

16.3.2  Enabling Event Listeners

Listeners can be enabled using hibernate.cfg.xml (till Hibernate3 ) .as below 

Add the <event> tag in hibernate.cfg.xml file under <session-factory> tag like below .

<event type="pre-update">
      <listener class="com.hibernate.tutorial.UpdateBookEventListener"/>
</event>

<event type="post-update">
     <listener class="com.hibernate.tutorial.UpdateBookEventListener"/>
</event>

With Hibernate 4 events configured in hibernate.cfg.xml files are ignored and custom integrator has to be used. Follow below steps

Create Custom Integrator by implementing Integrator interface

package com.tutorial.hibernate;

import org.hibernate.cfg.Configuration;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.event.service.spi.EventListenerRegistry;
import org.hibernate.event.spi.EventType;
import org.hibernate.integrator.spi.Integrator;
import org.hibernate.metamodel.source.MetadataImplementor;
import org.hibernate.service.spi.SessionFactoryServiceRegistry;

public class MyIntegrator implements Integrator {

        public void integrate(
            Configuration configuration,
            SessionFactoryImplementor sessionFactory,
            SessionFactoryServiceRegistry serviceRegistry) {

            final EventListenerRegistry eventListenerRegistry = serviceRegistry.getService( EventListenerRegistry.class );

            UpdateBookEventListener listener = new UpdateBookEventListener(); 

            eventListenerRegistry.appendListeners(EventType.PRE_UPDATE, listener );
            eventListenerRegistry.appendListeners(EventType.POST_UPDATE, listener );
        } 

        @Override
         public void disintegrate(SessionFactoryImplementor arg0,
            SessionFactoryServiceRegistry arg1) {        
        }

        @Override
         public void integrate(MetadataImplementor arg0,
            SessionFactoryImplementor arg1, SessionFactoryServiceRegistry arg2) {        
        }
}

b. Create a file with name org.hibernate.integrator.spi.Integrator inside META-INF/services directory . In this file you need to add the fully qualified class path of the integrator class like com.tutorial.hibernate.MyIntegrator

16.3.3 Example 

Test Program

import org.hibernate.Session;
Import org. hibernate. Session Builder;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.hibernate.dialect.lock.UpdateLockingStrategy;

import com.tutorial.hibernate.Book;
import com.tutorial.hibernate.ConsoleLogInterceptor;

public class Test {

    private static SessionFactory factory; 
    public static void main(String args[])
    {

        Configuration cfg = new Configuration().configure();
        factory = cfg.buildSessionFactory();
        Session session =     factory.openSession();
        Transaction tx = session.beginTransaction();

        Book book = new Book();
        book.setName("Hibernate tutorial");
        int id = (Integer)session.save(book);

        tx.commit();
        session.close();  

        session= factory.openSession();

        tx= session.beginTransaction();
        book = (Book)session.get(Book.class, id);
        book.setName("Updated");

        tx.commit();
        session.close();
        factory.close();
    }
}

 On running test program we can see the events fired

Like us on Facebook