Expand Authors Top

If you have a few years of experience in the Java ecosystem and you’d like to share that with the community, have a look at our Contribution Guidelines.

Expanded Audience – Frontegg – Security (partner)
announcement - icon User management is very complex, when implemented properly. No surprise here.

Not having to roll all of that out manually, but instead integrating a mature, fully-fledged solution - yeah, that makes a lot of sense.
That's basically what Frontegg is - User Management for your application. It's focused on making your app scalable, secure and enjoyable for your users.
From signup to authentication, it supports simple scenarios all the way to complex and custom application logic.

Have a look:

>> Elegant User Management, Tailor-made for B2B SaaS

NPI – JPA Buddy – JPA (partner)
announcement - icon

JPA is huge! It covers nearly every aspect of communication between relational databases and the Java application and is deeply integrated into all major frameworks.

If you're using IntelliJ, JPA Buddy is super helpful. The plugin gently guides you through the subtleties of the most popular JPA implementations, visually reminds you of JPA features, generates code that follows best practices, and integrates intelligent inspections to improve your existing persistence code.

More concretely, it provides powerful tooling to generate Spring Data JPA repositories and methods, Flyway Versioned Migrations, Liquibase Differential Changelogs, DDL and SQL statements, DTO objects, and MapStruct interfaces.

Oh, and it actually generates JPA entities from an existing database and gradually update the data model as the database evolves! Yeah.

>> Become a lot more productive with JPA Buddy

1. Overview

In this discussion, we'll look at various ways of intercepting operations within Hibernate's abstracted relational mapping implementation.

2. Defining Hibernate Interceptors

The Hibernate Interceptor is an interface that allows us to react to certain events within Hibernate.

These interceptors are registered as callbacks and provide communication links between Hibernate's session and application. With such a callback, an application can intercept core Hibernate's operations such as save, update, delete, etc.

There are two ways of defining interceptors:

  1. implementing the org.hibernate.Interceptor interface
  2. extending the org.hibernate.EmptyInterceptor class

2.1. Implementing an Interceptor Interface

Implementing org.hibernate.Interceptor requires implementing about 14 accompanying methods. These methods include onLoad, onSave, onDelete, findDirty, and a few more.

It's also important to ensure that any class that implements Interceptor interface is serializable (implements java.io.Serializable).

A typical example would look like:

public class CustomInterceptorImpl implements Interceptor, Serializable {

    public boolean onLoad(Object entity, Serializable id, 
      Object[] state, String[] propertyNames, Type[] types) 
      throws CallbackException {
        // ...
        return false;

    // ...

    public String onPrepareStatement(String sql) {
        // ...   
        return sql;


If there are no special requirements, extending the EmptyInterceptor class and only overriding the required methods is highly recommended.

2.2. Extending EmptyInterceptor

Extending the org.hibernate.EmptyInterceptor class provides an easier way of defining an interceptor. We now only need to override the methods that relate to the operation we want to intercept.

For example, we can define our CustomInterceptor as:

public class CustomInterceptor extends EmptyInterceptor {

And if we need to intercept data saving operations before they are executed, we need to override onSave method:

public boolean onSave(Object entity, Serializable id, 
  Object[] state, String[] propertyNames, Type[] types) {
    if (entity instanceof User) {
        logger.info(((User) entity).toString());
    return super.onSave(entity, id, state, propertyNames, types);

Notice how this implementation simply prints out the entity – if it's a User.

While it's possible to return a value of true or false, it's a good practice to allow propagation of onSave event by invoking super.onSave().

Another use-case would be providing an audit trail for database interactions. We can use the onFlushDirty() method to know when an entity changes.

For the User object, we can decide to update its lastModified date property whenever changes on entities of type User happen.

This can be achieved with:

public boolean onFlushDirty(Object entity, Serializable id, 
  Object[] currentState, Object [] previousState, 
  String[] propertyNames, Type[] types) {
    if (entity instanceof User) {
        ((User) entity).setLastModified(new Date());
        logger.info(((User) entity).toString());
    return super.onFlushDirty(entity, id, currentState, 
      previousState, propertyNames, types);

Other events such as delete and load (object initialization) can be intercepted by implementing the corresponding onDelete and onLoad methods respectively.

3. Registering Interceptors

A Hibernate interceptor can either be registered as Session-scoped or SessionFactory-scoped.

3.1. Session-scoped Interceptor

A Session-scoped interceptor is linked to a specific session. It's created when the session is being defined or opened as:

public static Session getSessionWithInterceptor(Interceptor interceptor) 
  throws IOException {
    return getSessionFactory().withOptions()

In the above, we explicitly registered an interceptor with a particular hibernate session.

3.2. SessionFactory-scoped Interceptor

A SessionFactory-scoped interceptor is registered before building a SessionFactory. This is typically done through the applyInterceptor method on a SessionFactoryBuilder instance:

ServiceRegistry serviceRegistry = configureServiceRegistry();
SessionFactory sessionFactory = getSessionFactoryBuilder(serviceRegistry)
  .applyInterceptor(new CustomInterceptor())

It's important to note that a SessionFactory-scoped interceptor will be applied to all sessions. Hence, we need to be careful not to store session specific state – as this interceptor will be used by different sessions concurrently.

For a session specific behavior, it's recommended to explicitly open a session with a different interceptor as earlier shown.

For SessionFactory-scoped interceptors, we naturally need to ensure that it's thread-safe. This can be achieved by specifying a session context in the properties file:


Or by adding this to our XML configuration file:

<property name="hibernate.current_session_context_class">

Also, to ensure serializability, SessionFactory-scoped interceptors must implement the readResolve method of the Serializable interface.

4. Conclusion

We've seen how to define and register Hibernate interceptors either as Session-scoped or SessionFactory-scoped. In either case, we must ensure that the interceptors are serializable especially if we want a serializable session.

Other alternatives to interceptors include Hibernate Events and JPA Callbacks.

And, as always, you can check out the complete source code over on Github.

Persistence bottom

Get started with Spring Data JPA through the reference Learn Spring Data JPA course:

Persistence footer banner
Comments are closed on this article!