Connecting Spring and Hibernate though BeanContainer

Recently I was working on a Spring Boot application and I wanted to put together Spring and Hibernate with JPA EntityListeners.

As you may know, any JPA EntityListener class is not in the scope of Spring or any Ioc, as long as you bring them together. If you have the requirement to do this, there are a lot of examples in blogs, describing workarounds like

http://esus.com/hibernate-4-integrator-pattern-springs-di/ or https://guylabs.ch/2014/02/22/autowiring-pring-beans-in-hibernate-jpa-entity-listeners which in the end are doing autowiring programmatically (i.e. by using org.springframework.web.context.support.SpringBeanAutowiringSupport).

In the recent versions of Spring and Hibernate, this work became obsolete (as I described also on https://stackoverflow.com/questions/47941717/spring-dependency-injection-into-jpa-entity-listener/55452014#55452014).

Since Hibernate 5.3 there is a so called org.hibernate.resource.beans.container.spi.BeanContainer, which makes Hibernate aware of Spring or CDI beans. Later, org.springframework.orm.hibernate5.SpringBeanContainer was added with Spring 5.1, so you do not need to handle autowiring any more. See details of this feature in https://github.com/spring-projects/spring-framework/issues/20852

As an example of a JPA EntityListener, you can add the @Component annotation and wire any Spring bean to it.

@Component

public class MyEntityListener{

  private MySpringBean bean;

  @Autowired

  public MyEntityListener(MySpringBean bean){

    this.bean = bean;

  }

  @PrePersist

  public void prePersist(final Object entity) {

    …

  }

}

To make Hibernate aware of this component bean, there are two scenarios:

1) You are using Spring Boot: Then you do not have to do anything, because the configuration of LocalContainerEntityManagerFactoryBean is done automatically in org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaConfiguration.

2) Outside of Spring Boot, you have to register SpringBeanContainer to Hibernate like this:

@Configuration

….

    @Bean

    public LocalContainerEntityManagerFactoryBean entityManagerFactory(ConfigurableListableBeanFactory beanFactory) {

            LocalContainerEntityManagerFactoryBean emfb = …

            emfb.getJpaPropertyMap().put(AvailableSettings.BEAN_CONTAINER, new SpringBeanContainer(beanFactory));

            …

            return emfb;

            }