Spring managed event listeners with JPA

Hi readers,

I wanted to set things right. I’ve  been approximative in a previous post. I wrote an incomplete assertion: “using spring-managed listeners with JPA is not possible“. I want to clarify things:

- using spring managed listeners with JPA is not possible if you absolutly want to use JPA interface and ignore the underlying implementation,

- if your implementation is hibernate, and depending on hibernate version, your solution may be more or less elegant.

1 – When JPA won’t suit your needs.

JPA won’t suit your needs when you need (for many reason) to have control on the listener : the class that executes instructions as a reaction to an event.
You also may want to inject already live beans (collaborators) in the listener. If so you can’t let JPA do that wiring.

But even if you cannot provide JPA interfaces with your listeners, you’re not stuck, you can use the underlying implementation.
The key is to obtain the implementation from the interface: downcasting is a bad but can be helpful in some cases.

2 – Use Hibernate < 4.0.0.Final

The script below shows how to register spring-managed listeners in a hibernate (< 4.0.0.Final) configuration obtained from JPA.
Beware that the below code doesn’t extend hibernate’s behaviour, it modifies it: it overrides the existings Listeners array rather than extending them. A cleaner solution would leave hibernate’s behaviour intact and add listeners to existing ones.
The example uses @Autowired annotations but the same configuration can be achived with xml.

import javax.annotation.PostConstruct;
import javax.persistence.EntityManagerFactory;

import org.hibernate.ejb.HibernateEntityManagerFactory;
import org.hibernate.impl.SessionFactoryImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * @author louis.gueye@gmail.com
 */
@Component
public class HibernateListenersConfigurer {

    @Autowired
    private EntityManagerFactory	entityManagerFactory;

    @Autowired
    private PreInsertEventListener	preInsertEventListener;

    @Autowired
    private PreUpdateEventListener	preUpdateEventListener;

    @Autowired
    private PreDeleteEventListener	preDeleteEventListener;

    @PostConstruct
    public void registerListeners() {
	HibernateEntityManagerFactory hibernateEntityManagerFactory = (HibernateEntityManagerFactory) this.entityManagerFactory;
	SessionFactoryImpl sessionFactoryImpl = (SessionFactoryImpl) hibernateEntityManagerFactory.getSessionFactory();
	sessionFactoryImpl.getEventListeners().setPreInsertEventListeners(new org.hibernate.event.PreInsertEventListener[] { this.preInsertEventListener });
	sessionFactoryImpl.getEventListeners().setPreUpdateEventListeners(new org.hibernate.event.PreUpdateEventListener[] { this.preUpdateEventListener });
	sessionFactoryImpl.getEventListeners().setPreDeleteEventListeners(new org.hibernate.event.PreDeleteEventListener[] { this.preDeleteEventListener });
	}

}

3 – Use Hibernate 4.0.0.Final and above

The script below shows how to register spring-managed listeners in an hibernate configuration obtained from JPA.
The new concept here is SPI (Service Provider Interface) which basically is a well defined extension point. It’s a very nice illustration of the Open/Closed Principle.
Hibernate provides many SPI. Among them is the event SPI which defines listeners interfaces (more than 20), events (about 30) and an EventListenerRegistry.

import javax.annotation.PostConstruct;
import javax.persistence.EntityManagerFactory;
import org.hibernate.ejb.HibernateEntityManagerFactory;
import org.hibernate.impl.SessionFactoryImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import org.hibernate.event.service.spi.EventListenerRegistry;
...
/**
 * @author louis.gueye@gmail.com
 */
@Component
public class HibernateListenersConfigurer {

    @Autowired
    private EntityManagerFactory entityManagerFactory;
    @Autowired
    private SomeHibernateListener listener;

@PostConstruct
public void registerListeners() {
    HibernateEntityManagerFactory hibernateEntityManagerFactory = (HibernateEntityManagerFactory) this.entityManagerFactory;
    SessionFactoryImpl sessionFactoryImpl = (SessionFactoryImpl) hibernateEntityManagerFactory.getSessionFactory();
    EventListenerRegistry registry = sessionFactoryImpl.getServiceRegistry().getService(EventListenerRegistry.class);
    registry.getEventListenerGroup(EventType.POST_COMMIT_INSERT).appendListener(listener);
    registry.getEventListenerGroup(EventType.POST_COMMIT_UPDATE).appendListener(listener);
}

Hope I made it clearer for the interested ones.

Credits

The below articles were helpful :
Eventlisteners using hibernate 4-0 with spring-3-1-0-release : a stackoverflow thread on the subject.

- Event Listener Registration : an example from the source (an hibernate core developper Steve Ebersole). The link on JPAIntegrator is broken because of an hibernate refactoring. Use this one instead.

7 comments on “Spring managed event listeners with JPA

  1. coralf says:

    Thanks for this.

    While the issue appear to be well documented for hibernate 3, it is less documented for hibernate 4 and even less so when using the JPA wiring with LocalContainerEntityManagerFactoryBean and HibernateJpaVendorAdapter, which is where I got stuck.

    There are also very few examples of @Component and @Configuration solutions.

    Thanks for sharing

  2. Darrin Welch says:

    Hibernate Entity Manager needs to enhance Hibernate core to implements all the JPA semantics. It does that through the event listener system of Hibernate. Be careful when you use the event system yourself, you might override some of the JPA semantics. A safe way is to add your event listeners to the list given below.

  3. [...] over again. After some googlin’ I found out about the Service Provider Interface in Hibernate [2]. A feature available since version 4, great and [...]

  4. […] Enter a blog entry from nearly two years ago: Spring managed event listeners with JPA. […]

  5. Prog Mania says:

    First I’d like to thank you for this interesting topic, but I have a question.
    What type of interface should some listener implement?

    • louis gueye says:

      I can’t tell the exact packge but you should be able to implement some pre/post CRUD event listener from the hibernate framework for example org.hibernate.event.spi.PreDeleteEventListener
      Hope this helps

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s