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.