Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Hibernate interceptor or listener with Spring Boot and Spring Data JPA

I'd like to run some checks prior to saving a collection of children of an object (cascade = all).

I am using Spring Boot and Spring Data JPA and was wondering what approach would be the best: a Hibernate listener or an interceptor. What are the pros/cons of each ? Do you happen to have an example for the one you consider the best approach ?

I have used Hibernate listeners before configured in XML like this:

    <property name="eventListeners">
        <map>
            <entry key="post-update">
                <list>
                    <ref bean="myListener" />
                </list>
            </entry>
        </map>
    </property>

on the session factory (older project). But now most of my configs are in annotations (cause Spring Boot) and I want to keep the configs as simple and light as possible, so maybe an interceptor would be a better solution.

Thank you.

like image 407
ccc Avatar asked Sep 23 '16 14:09

ccc


People also ask

Should we use Hibernate or spring data JPA?

JPA uses EntityManager interface to create/read/delete operation and maintains the persistence context. Hibernate uses Session interface to create/read/delete operation and maintains the persistence context. JPA uses JPQL (Java Persistence Query Language) as Object Oriented Query language for database operations.

Can we use spring data JPA and Hibernate together?

You cant actually use both of them in the same application. For backwards compatibility. We are expanding our application and want to start using Spring Data JPA, but still keep the old hibernate implementation. Its better you develop your new application as a separate microservice and use spring data jpa ..

Why spring data JPA is better than Hibernate?

Hibernate is a JPA provider and ORM that maps Java objects to relational database tables. Spring Data JPA is an abstraction that makes working with the JPA provider less verbose. Using Spring Data JPA you can eliminate a lot of the boilerplate code involved in managing a JPA provider like Hibernate.

Does spring boot use JPA or Hibernate?

By default, Spring uses Hibernate as the default JPA vendor. you can see hibernate related dependency in dependency hierarchy under the pom. xml, this will resolve all dependencies of your project.


2 Answers

I did a lot of looking around on this for myself and thought I'd share what I got working (I included the helpful (non-inline) links at the bottom).

Interceptor

To use an interceptor, you extend the org.hibernate.EmptyInterceptor class and override the methods you want to intercept. You probably want onSave(...) in your case.

package foo.bar;

import org.hibernate.EmptyInterceptor;
import org.hibernate.type.Type;
import java.io.Serializable;

public class MyInterceptor extends EmptyInterceptor {
    @Override
    public boolean onSave(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) {
        // do your checks here
        return false;
    }
}

You have to register your interceptor with Spring/Hibernate. You can do this in your application.properties or application.yml.

spring:
  jpa:
    properties:
      hibernate.ejb.interceptor: foo.bar.MyInterceptor

The upsides to an interceptor are that it is (potentially) less code and relatively simple configuration. The downsides are that you can only have one for your entire application and the API can be confusing to work with.

Event Listener

For events, you implement one of Hibernate's org.hibernate.event.spi.*Listener interfaces. You probably want the org.hibernate.event.spi.PreInsertEventListener in your case.

You have to register your event in the EventListenerRegistry. To do this, you can make your class a @Component, @Autowire the EntityManagerFactory into your class, and create a @PostConstruct method to register your class.

package foo.bar;

import org.hibernate.event.service.spi.EventListenerRegistry;
import org.hibernate.event.spi.EventType;
import org.hibernate.event.spi.PreInsertEvent;
import org.hibernate.event.spi.PreInsertEventListener;
import org.hibernate.internal.SessionFactoryImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.persistence.EntityManagerFactory;

@Component
public class MyEventListener implements PreInsertEventListener {
    @Autowired
    private EntityManagerFactory entityManagerFactory;

    @PostConstruct
    private void init() {
        SessionFactoryImpl sessionFactory = entityManagerFactory.unwrap(SessionFactoryImpl.class);
        EventListenerRegistry registry = sessionFactory.getServiceRegistry().getService(EventListenerRegistry.class);
        registry.getEventListenerGroup(EventType.PRE_INSERT).appendListener(this);
    }

    @Override
    public boolean onPreInsert(PreInsertEvent preInsertEvent) {
        // do your checks here
        return false;
    }
}

The upsides to listeners are that you can have as many as you want, the API is nicer than the interceptor's, and the code and the configuration are all in one place. The downside is that the configuration is longer and more involved.


  • https://docs.jboss.org/hibernate/orm/3.5/reference/en-US/html/events.html
  • How to use Spring managed Hibernate interceptors in Spring Boot?
  • https://dzone.com/articles/spring-managed-hibernate-event-listeners
like image 129
Dillon Ryan Redding Avatar answered Oct 19 '22 05:10

Dillon Ryan Redding


Hello,

First of all you can check the: https://www.baeldung.com/database-auditing-jpa where every options is explained in detail.

I would personally recommend Hibernate Interceptor, easy to use and understand. Depending on the complexity of the project, in most cases it will do.

In order to configure this in your application you simply need to add: spring.jpa.properties.hibernate.ejb.interceptor = path.to.interceptor (in application.properties). The interceptor itself should be @Component.

As long as the interceptor doesn't actually use any beans. Otherwise it is a bit more complicated but I would be more than happy to offer the solution.

Don't forget to add in application-test.properties, an EmptyInterceptor to not use the logging system (or whatever you want to use it for) in tests (which wouldn't be very helpful).

Hope this was of use to you.

As a final note: always update your Spring / Hibernate versions (use the latest as possible) and you will see that most code will become redundant as newer versions try to reduce the configurations as much as possible.

like image 5
Rareș Flueraș Avatar answered Oct 19 '22 07:10

Rareș Flueraș