Spring managed Hibernate interceptor in JPA

I have been trying to teach Hibernate injecting dependencies into Entities (I know, there is magic @Configurable annotation, I wanted to try it without magic). It is quite easy to do it using Hibernate interceptor (for example like this). But there is one drawback. It is not straightforward to inject interceptor into Hibernate when JPA abstraction is in the way.

It is simple to define interceptor in persistence.xml using hibernate.ejb.interceptor property. But it is only possible to specify class name, you can not inject Spring bean. If you read documentation to Spring LocalContainerEntityManagerFactoryBean there is no possibility to specify the interceptor bean there neither. But there is a way how to achieve it. First of all, you have to redefine Hibernate PersistenceProvider.

public class ConfigurableHibernatePersistence extends HibernatePersistence {
	private Interceptor interceptor;
	public Interceptor getInterceptor() {
		return interceptor;
	}

	public void setInterceptor(Interceptor interceptor) {
		this.interceptor = interceptor;
	}

	@SuppressWarnings("unchecked")
	@Override
	public EntityManagerFactory createContainerEntityManagerFactory(PersistenceUnitInfo info, Map map) {
		Ejb3Configuration cfg = new Ejb3Configuration();
		Ejb3Configuration configured = cfg.configure( info, map );
		postprocessConfiguration(info, map, configured);
		return configured != null ? configured.buildEntityManagerFactory() : null;
	}

	@SuppressWarnings("unchecked")
	protected void postprocessConfiguration(PersistenceUnitInfo info, Map map, Ejb3Configuration configured) {
		if (this.interceptor != null)
		{
			if (configured.getInterceptor()==null || EmptyInterceptor.class.equals(configured.getInterceptor().getClass()))
			{
				configured.setInterceptor(this.interceptor);
			}
			else
			{
				throw new IllegalStateException("Hibernate interceptor already set in persistence.xml ("+configured.getInterceptor()+")");
			}
		}
	}
}

(source)

Here we override method createContainerEntityManagerFactory in order to add postprocessConfiguration method call. In this method, it is possible to change Hibernate configuration as needed. Now the only thing to be done is configuring Spring to use our new PersistenceProvider. It is quite simple.

	<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
		<property name="persistenceUnitName" value="testPU" />
		<property name="dataSource" ref="dataSource" />
		<property name="persistenceProvider">
			<bean class="net.krecan.javacrumbs.jpa.interceptor.ConfigurableHibernatePersistence">
				<property name="interceptor">
					<bean class="net.krecan.javacrumbs.jpa.interceptor.SpringInjectingInterceptor"/>
				</property>
			</bean>
		</property>
	</bean>

And that all folks, we have Spring configured interceptor even when using JPA abstraction. It would be better if similar classes were in Spring or Hibernate, but it's just few lines of code so feel free to copy them if you need to. If you know easier way how to do it, please mention it in the comments below.

Tags: , , , ,

6 Responses to “Spring managed Hibernate interceptor in JPA”

  1. c0va Says:

    Nice piece of code, exactly what I need. Thanks for sharing it, you saved me lot of time.

  2. Arnaud Tournier Says:

    Thanks for your help, this piece of code was of great value to me !!!

  3. Ramon Turnes Says:

    Thank you as well, I needed to integrate JPA with exiting hibernate interceptors and this was giving me a lot of headaches.

  4. Niki Says:

    Thank you very much! I have to upgrade to JPA 2.1 (Hibernate 4.3) soon and wanted to know whats the way to achieve this, because Ejb3Configuration was deprected and deleted with version 4.3 (I know it's only a beta atm., but will have to upgrade as soon as final version is released). I just took a short look at new API, but haven't found a solution so far

  5. Gilmar Says:

    Nice code, great job!

  6. Jonas Says:

    @Niki: Just override LocalContainerEntityManagerFactoryBean with a method:

    public void setInterceptor(Interceptor interceptor){
    getJpaPropertyMap().put("hibernate.ejb.interceptor", interceptor);
    }

Leave a Reply

Current day month ye@r *