Hibernate 5 - Event listener example

Posted on November 18, 2017


In last post, we learned how to configure the interceptor in Hibernate application. Interceptors in Hibernate are primarily used to inspect the object properties before some events. 

Interceptor’s methods such as onSave(), onLoad() etc. are invoked by Hibernate default event listeners. For example, the Interceptor#onLoad() is called by the DefaultPreLoadEventListener.

Hibernate native event system is more flexible then interceptor. It allows us to implement custom event listeners to listen the PreLoadEvent, PostLoadEvent, PostInsertEvent and more.

Let’s see an example how to implement the event listener in Hibernate application.

Tools and technologies used for this application are - 

  • Hibernate ORM 5.2.12.Final
  • Log4j 2.8.2
  • JavaSE 1.8
  • MySQL Server 5.7.12
  • Eclipse Oxygen.1
  • Maven 3.3.9

Project structure

Final project structure of our application will look like as follows.

hibernate5-event.png

Jar dependencies

Add the following jar dependencies in pom.xml file of your maven project.

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.boraji.tutorial.hibernate</groupId>
  <artifactId>hibernate-event-example</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <name>hibernate-event-example</name>

  <dependencies>
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-core</artifactId>
      <version>5.2.12.Final</version>
    </dependency>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>6.0.6</version>
    </dependency>
    <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-core</artifactId>
      <version>2.8.2</version>
    </dependency>
    <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-api</artifactId>
      <version>2.8.2</version>
    </dependency>
  </dependencies>

  <build>
    <sourceDirectory>src/main/java</sourceDirectory>
    <plugins>
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.5.1</version>
        <configuration>
          <source>1.8</source>
          <target>1.8</target>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>

Entity class

Create a simple @Entity class as follows.

package com.boraji.tutorial.hibernate.entity;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class Book {
   
   @Id
   @GeneratedValue(strategy = GenerationType.IDENTITY)
   private long id;

   private String title;
   private String author;

   //Getter and Setter methods
   
   public String toString() {
      return "Book [id=" + id + ", title=" + title + ", author=" + author + "]";
   }
}

 

Implementing event listeners

In Hibernate, all event listener interfaces are available in org.hibernate.event.spi package. You can use any event listener based on the org.hibernate.event.spi.EventType.

In this example, I will show you how to implement the LoadEventListener, SaveOrUpdateEventListener and RefreshEventListener as follows.

LoadEventListenerImp.java

package com.boraji.tutorial.hibernate.event;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.hibernate.HibernateException;
import org.hibernate.event.spi.LoadEvent;
import org.hibernate.event.spi.LoadEventListener;

import com.boraji.tutorial.hibernate.entity.Book;

public class LoadEventListenerImp implements LoadEventListener {
   
   private static final long serialVersionUID = 1L;
   private static Logger logger = LogManager.getLogger(LoadEventListenerImp.class);

   @Override
   public void onLoad(LoadEvent e, LoadType type) throws HibernateException {
      logger.info("onLoad is called.");
      Object obj = e.getResult();
      if (obj instanceof Book) {
         Book book = (Book) obj;
         logger.info(book);
      }
   }

}

SaveUpdateEventListenerImp.java

package com.boraji.tutorial.hibernate.event;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.hibernate.HibernateException;
import org.hibernate.event.spi.SaveOrUpdateEvent;
import org.hibernate.event.spi.SaveOrUpdateEventListener;

import com.boraji.tutorial.hibernate.entity.Book;

public class SaveUpdateEventListenerImp implements SaveOrUpdateEventListener {
   
   private static Logger logger = LogManager
            .getLogger(SaveUpdateEventListenerImp.class);
   private static final long serialVersionUID = 1L;

   @Override
   public void onSaveOrUpdate(SaveOrUpdateEvent e) throws HibernateException {

      logger.info("onSaveOrUpdate is called.");

      Object obj = e.getEntity();
      if (obj instanceof Book) {
         Book book = (Book) obj;
         logger.info(book);
      }
   }
}

RefreshEventListenerImp.java

package com.boraji.tutorial.hibernate.event;

import java.util.Map;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.hibernate.HibernateException;
import org.hibernate.event.spi.RefreshEvent;
import org.hibernate.event.spi.RefreshEventListener;

import com.boraji.tutorial.hibernate.entity.Book;

public class RefreshEventListenerImp implements RefreshEventListener {
   
   private static final long serialVersionUID = 1L;
   private static Logger logger = LogManager
            .getLogger(RefreshEventListenerImp.class);

   @Override
   public void onRefresh(RefreshEvent e) throws HibernateException {
      logger.info("onRefresh is called.");
      Object obj = e.getObject();
      if (obj instanceof Book) {
         Book book = (Book) obj;
         logger.info(book);
      }
   }

   @SuppressWarnings("rawtypes")
   @Override
   public void onRefresh(RefreshEvent e, Map refreshedAlready) 
         throws HibernateException {
      logger.info("onRefresh is called.");
   }

}

Registering event listeners

To register event listeners, you need to create an integrator class by implement the org.hibernate.integrator.spi.Integrator interface as follows.

EventListenerIntegrator.java

package com.boraji.tutorial.hibernate;

import org.hibernate.boot.Metadata;
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.service.spi.SessionFactoryServiceRegistry;

import com.boraji.tutorial.hibernate.event.LoadEventListenerImp;
import com.boraji.tutorial.hibernate.event.RefreshEventListenerImp;
import com.boraji.tutorial.hibernate.event.SaveUpdateEventListenerImp;

public class EventListenerIntegrator implements Integrator {

   @Override
   public void integrate(Metadata metadata, SessionFactoryImplementor 
         sessionFactory, SessionFactoryServiceRegistry serviceRegistry) {

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

      eventListenerRegistry.getEventListenerGroup(EventType.SAVE)
                     .appendListener(new SaveUpdateEventListenerImp());
      
      eventListenerRegistry.getEventListenerGroup(EventType.LOAD)
                     .appendListener(new LoadEventListenerImp());
      
      eventListenerRegistry.getEventListenerGroup(EventType.REFRESH)
                     .appendListener(new RefreshEventListenerImp());
   }

   @Override
   public void disintegrate(SessionFactoryImplementor sessionFactory,
         SessionFactoryServiceRegistry serviceRegistry) {

   }
}

 

Hibernate Utility class

Create a helper class HibernateUtil to bootstrap hibernate.

Add the EventListenerIntegrator using the BootstrapServiceRegistryBuilder#applyIntegrator() method to be applied to the bootstrap registry.

Map the Book entity using the #MetadataSources.addAnnotatedClass() method.

HibernateUtil.java

package com.boraji.tutorial.hibernate;

import java.util.HashMap;
import java.util.Map;

import org.hibernate.SessionFactory;
import org.hibernate.boot.Metadata;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.BootstrapServiceRegistry;
import org.hibernate.boot.registry.BootstrapServiceRegistryBuilder;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Environment;

import com.boraji.tutorial.hibernate.entity.Book;

public class HibernateUtil {

   private static StandardServiceRegistry registry;
   private static SessionFactory sessionFactory;

   public static SessionFactory getSessionFactory() {
      if (sessionFactory == null) {
         try {
            BootstrapServiceRegistry bootstrapRegistry =
                  new BootstrapServiceRegistryBuilder()
                  .applyIntegrator(new EventListenerIntegrator())
                  .build();
            
            StandardServiceRegistryBuilder registryBuilder = 
                  new StandardServiceRegistryBuilder(bootstrapRegistry);

            Map<String, Object> settings = new HashMap<>();
            settings.put(Environment.DRIVER, "com.mysql.cj.jdbc.Driver");
            settings.put(Environment.URL, "jdbc:mysql://localhost:3306/BORAJI?useSSL=false");
            settings.put(Environment.USER, "root");
            settings.put(Environment.PASS, "admin");
            settings.put(Environment.HBM2DDL_AUTO, "update");
            
            registryBuilder.applySettings(settings);

            registry = registryBuilder.build();

            MetadataSources sources = new MetadataSources(registry)
                  .addAnnotatedClass(Book.class);

            Metadata metadata = sources.getMetadataBuilder().build();

            sessionFactory = metadata.getSessionFactoryBuilder().build();
         } catch (Exception e) {
            if (registry != null) {
               StandardServiceRegistryBuilder.destroy(registry);
            }
            e.printStackTrace();
         }
      }
      return sessionFactory;
   }

   public static void shutdown() {
      if (registry != null) {
         StandardServiceRegistryBuilder.destroy(registry);
      }
   }
}

 

Logging event listeners

To enable logging, create a log4j2.xml file under src/main/resources folder and write the following code in it.

log4j2.xml

<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
  <Appenders>
    <!-- Console Appender -->
    <Console name="Console" target="SYSTEM_OUT">
      <PatternLayout
        pattern="%d{yyyy-MMM-dd HH:mm:ss a}  %-5level %logger{36} - %msg%n" />
    </Console>

  </Appenders>
  <Loggers>
    <!-- Log everything in hibernate -->
    <Logger name="com.boraji.tutorial.hibernate" level="trace"
      additivity="false">
      <AppenderRef ref="Console" />
    </Logger>

    <Root level="error">
      <AppenderRef ref="Console" />
    </Root>
  </Loggers>
</Configuration>

Run Application

Create main class to test the registered event listeners.

MainApp.java

package com.boraji.tutorial.hibernate;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.query.Query;

import com.boraji.tutorial.hibernate.entity.Book;

public class MainApp {
   private static Logger logger = LogManager.getLogger(MainApp.class);

   public static void main(String[] args) {

      Session session = null;
      Transaction transaction = null;
      try {
         session = HibernateUtil.getSessionFactory().openSession();

         transaction = session.getTransaction();
         transaction.begin();

         // Save Book
         Book book = new Book();
         book.setTitle("Thinking in Java");
         book.setAuthor("Bruce Eckel");
         session.save(book);

         // Load Book
         Book book2 = session.get(Book.class, 1l);
         logger.info("Auther name before update - "+book2.getAuthor());
        
         // Update Book
         Query<?> query=session.createQuery("update Book set author=LOWER(author)"
               + " where id=:id");
         query.setParameter("id", book2.getId());
         query.executeUpdate();
         
         //Refresh Book
         session.refresh(book2);
         logger.info("Auther name after update - "+book2.getAuthor());

         transaction.commit();
      } catch (Exception e) {
         if (transaction != null) {
            transaction.rollback();
         }
         e.printStackTrace();
      } finally {
         if (session != null) {
            session.close();
         }
      }
      HibernateUtil.shutdown();
   }

}

After executing the MainApp, the output of your program will look like as follows.

hibernate5-event_01.png

Download Sources