Hibernate Entity Lifecycle Events and Callbacks

Learn to execute custom methods whenever an Entity’s state is changed by the Session using one of the CRUD methods. These methods are called callback methods that are executed by hibernate’s event architecture.

1. Native Event Architecture

Whenever we call methods in the Session interface to persist, update or delete the entities; the session generates an appropriate event based on the executed method and passes it to the configured event listener(s) for that type.

The event types are declared as enum values on org.hibernate.event.spi.EventType.

For example, when we persist an entity using the session.persist() method then an event EventType.PERSIST is generated. If there are any PersistEventListener implementation registered for that Entity then the event is passed to that listener for processing.

A listener can be shared between multiple entities so it is recommended that listeners should be stateless.

1.1. Creating Event Listener

We have TransactionEntity and we are interested in listening to an event whenever the entity is persisted. We are implementing the PersistEventListener and defining the callback methods.

public class PersistTransactionListerner implements PersistEventListener {

  Logger LOG = LoggerFactory.getLogger(TransactionEntity.class);

  @Override
  public void onPersist(PersistEvent persistEvent) throws HibernateException {
    LOG.info("PersistTransactionListerner.onPersist() invoked !!");
    TransactionEntity transaction =
        (TransactionEntity) persistEvent.getObject();

    if (transaction.getDuration() == null) {
      transaction.setDuration(Duration.between(transaction.getStartTS(),
          transaction.getEndTS()));
    }
  }

  @Override
  public void onPersist(PersistEvent persistEvent,
                        PersistContext persistContext) throws HibernateException {
    LOG.info("PersistTransactionListerner.onPersist() invoked !!");
  }
}

1.2. Registering Event Listeners

To register the event listeners, we need to create our own implementation of org.hibernate.integrator.spi.Integrator interface. The main use of Integrator right now is registering event listeners only.

public class AppIntegrator implements Integrator {
  @Override
  public void integrate(
      Metadata metadata,
      SessionFactoryImplementor sessionFactory,
      SessionFactoryServiceRegistry serviceRegistry) {

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

    eventListenerRegistry.prependListeners(EventType.PERSIST,
        PersistTransactionListerner.class);
  }

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

The Integrator is then registered with the persistent context with the help of BootstrapServiceRegistryBuilder. Note that BootstrapServiceRegistry is intended to hold mainly 3 services that Hibernate needs at both bootstrap and run time.

  • ClassLoaderService
  • IntegratorService
  • StrategySelector
BootstrapServiceRegistryBuilder bootstrapRegistryBuilder =
    new BootstrapServiceRegistryBuilder();

bootstrapRegistryBuilder.applyIntegrator(new AppIntegrator());

BootstrapServiceRegistry bootstrapRegistry =
    bootstrapRegistryBuilder.build();

Now supply the custom BootstrapServiceRegistry instance to the StandardServiceRegistry and build the SessionFactory.

StandardServiceRegistry standardRegistry
    = new StandardServiceRegistryBuilder(bootstrapRegistry)
    .configure("hibernate-test.cfg.xml")
    .build();

Metadata metadata = new MetadataSources(standardRegistry)
    .addAnnotatedClass(TransactionEntity.class)
    .getMetadataBuilder()
    .build();

sessionFactory = metadata
    .getSessionFactoryBuilder().build();

2. Annotation Based Callbacks

2.1. Callback Annotations

The Jakarta persistence API defines a set of callbacks through annotations.

  • @PrePersist: Executed before the persist operation is executed or cascaded.
  • @PreRemove: Executed before the remove operation is executed or cascaded.
  • @PreUpdate: Executed before the update operation.
  • @PostPersist: Executed after the persist operation.
  • @PostRemove: Executed after the remove operation.
  • @PostUpdate: Executed after the update operation.
  • @PostLoad: Executed after an entity has been loaded or refreshed into the current persistence context.

2.2. Applying Annotations

We can use these annotations in two ways:

  • Annotate methods on the entity itself to receive notifications.
  • Annotate methods in a separate class. Then we can use this class as an entity listener class for multiple entities. Make sure that the entity listener is a stateless class with a no-arg constructor.

We can mix both approaches and use both together as well.

2.2.1. Annotate Entity Methods

In the following example, we are creating a @PostLoad callback method.

@Entity
@Table(name = "TBL_TRANS")
public class TransactionEntity {

  //Fields and other accessor methods

  @PostLoad
  public void updateDuration() {
      if(duration == null) {
         duration = Duration.between(startTS, endTS);
      }
  }
}

2.2.2. Annotate Methods is Separate Listener Class

Or we can create a separate event listener class TransactionListener that can be used to register with multiple entities if needed.

public class TransactionListener {

  Logger LOG = LoggerFactory.getLogger(TransactionEntity.class);
  
  @PostLoad
  public void updateDuration(Object entity) {
    if (entity != null && entity instanceof TransactionEntity) {
      TransactionEntity transaction = (TransactionEntity) entity;
      LOG.info("TransactionListner.updateDuration() invoked !!");
      transaction.setDuration(Duration.between(transaction.getStartTS(),
          transaction.getEndTS()));
    }
  }
}

And register the TransactionListener with required entities.

@Entity
@Table(name = "TBL_TRANS")
@EntityListeners({TransactionListener.class})
public class TransactionEntity {
   //..
}

3. Conclusion

In this hibernate tutorial, we learned to use the native event callback mechanism and register it to the SessionFactory using the Integrator API.

We also learned about annotation-based callback events from the new Jakarta persistence API as well as JPA.

Happy Learning !!

Sourcecode on Github

Was this post helpful?

Join 7000+ Awesome Developers

Get the latest updates from industry, awesome resources, blog updates and much more.

* We do not spam !!

Leave a Comment

HowToDoInJava

A blog about Java and related technologies, the best practices, algorithms, and interview questions.