springhibernatespring-bootjpalistener

Spring Boot 2.1.2, Hibernate 5: Register Hibernate Event Listeners (Insert/Update/Delete)


When I was using Hibernate 4 in a previous Spring application, the following was sufficient to register all interception of DB Insert/Update/Delete events. There was a simple Configuration class and the actual Interceptor implementation.

Config class

@Component
public class HibernateEntityEventListenerConfig {

    @Autowired
    private SessionFactory sessionFactory;

    @Autowired
    private HibernateEntityEventListener entityEventListener;

    @PostConstruct
    public void registerListeners() {
          final EventListenerRegistry registry = ((SessionFactoryImpl) sessionFactory)
                    .getServiceRegistry().getService(EventListenerRegistry.class);
            registry.getEventListenerGroup(EventType.PRE_INSERT).appendListener((PreInsertEventListener) entityEventListener);
            registry.getEventListenerGroup(EventType.PRE_UPDATE).appendListener((PreUpdateEventListener) entityEventListener);
            registry.getEventListenerGroup(EventType.PRE_DELETE).appendListener((PreDeleteEventListener) entityEventListener);
    }

}

Interceptor class

@Component
public class HibernateEntityEventListener implements PreInsertEventListener,
                                                     PreUpdateEventListener,    
                                                     PreDeleteEventListener {

@Override
public boolean onPreInsert(PreInsertEvent event) {
  //...
}

@Override
public boolean onPreUpdate(PreUpdateEvent event) {
  //...
}

@Override
public boolean onPreDelete(PreUpdateEvent event) {
  //...
}

But when I ported this code to a Spring Boot 2.1.2 app using Hibernate 5, I started getting the following startup error about SessionFactory not being found:

Field sessionFactory in util.HibernateEntityEventListenerConfig required 
a bean of type 'org.hibernate.SessionFactory' that could not be found.

The injection point has the following annotations:
    - @org.springframework.beans.factory.annotation.Autowired(required=true)


Action:

Consider defining a bean of type 'org.hibernate.SessionFactory' in your configuration.

I googled around and I only found some more elaborate and messy code that I don't trust or understand, https://stackoverflow.com/a/48471227/1005607

This is way too complicated now and I just need a simple way to wire this interceptor. What's the right way to put in the interceptors and get past this SessionFactory error?


Solution

  • I found the solution on Bozho's blog, the code can stay mostly the same as before, but I can't autowire SessionFactory anymore: I have to autowire EntityManagerFactory instead, and then "unwrap" it as SessinFactoryImpl sf = emf.unwrap(..). That's the only change.

    Latest code: Configuration:

    @Component   
    public class HibernateEntityEventListenerConfig {
    
    
    
    @PersistenceUnit
    private EntityManagerFactory emf;  // NOTE Can't autowire SessionFactory.
    
    @Autowired
    private HibernateEntityEventListener entityEventListener;
    
    @PostConstruct
    public void registerListeners() {
        // NOTE the emf.unwrap() to get the SessionFactoryImpl
        SessionFactoryImpl sessionFactory = emf.unwrap(SessionFactoryImpl.class);
        EventListenerRegistry registry = ((SessionFactoryImpl) sessionFactory)
                    .getServiceRegistry().getService(EventListenerRegistry.class);
            registry.getEventListenerGroup(EventType.PRE_INSERT).appendListener((PreInsertEventListener) entityEventListener);
            registry.getEventListenerGroup(EventType.PRE_UPDATE).appendListener((PreUpdateEventListener) entityEventListener);
            registry.getEventListenerGroup(EventType.PRE_DELETE).appendListener((PreDeleteEventListener) entityEventListener);
    }
    
    }