<bean id="hibernateSessionFactory" class="org.springframework.orm.hibernate3.annotation.LocalSessionFactoryBean"> ... <property name="eventListeners"> <map> <entry key="post-load"> <list> <bean ref="myPostLoadEventListenerBean" /> </list> </map> </property> </bean>
However, after moving to a JPA-based configuration, and replacing the Hibernate-specific LocalSessionFactoryBean with a JPA-specific LocalContainerEntityManagerFactoryBean, the only way to declaratively specify the Hibernate event listeners is via the jpaPropertyMap. However, with this method, the event listeners can no longer be Spring-instantiated objects, but rather can only be specified as class names, which Hibernate will "conveniently" use to instantiate the event listener objects on its own. This is hardly convenient, and in the case where one's custom event listener classes rely upon Spring's dependency injection for initialization, Hibernate's initialization mechanism is downright limiting. What we would like to do, but cannot, is specify our event listener beans within the JPA-compliant jpaPropertyMap, as follows:
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> ... <property name="persistenceProvider"> <bean class="org.hibernate.ejb.ConfigurableListenerBeansHibernatePersistence"> <property name="postLoadEventListeners"> <list> <bean ref="myPostLoadEventListener" /> </list> </property> </bean> </property> <property name="jpaPropertyMap"> <map> <entry key="hibernate.ejb.event.post-load"> <list> <bean class="org.hibernate.ejb.event.EJB3PostLoadEventListener" /> <bean ref="myPostLoadEventListener" /> </list> </entry> </map> </property> </bean>The limitation is ultimately introduced by Hibernate's EventListenerConfigurator, which only knows how to handle event listener class names, rather than event listener objects. That's fine, I suppose, but Spring should provide some factory class of its own that allows the configuration of JPA provider-specific properties (such as Hibernate's event listeners), to get around this limitation by taking the desired beans and then programmatically updating the underlying PersistenceProvider. But Spring does not provide this, as the LocalContainerEntityManagerFactoryBean simply passes along the jpaProperties without further consideration. In the end, Spring and Hibernate classes collude in a such a way that there is no way to accomplish what was easily done with the Hibernate-specific Spring configuration.
The solution I've come up with involves replacing Hibernate's HibernatePersistence class with an extended version of the class, ConfigurableListenerBeansHibernatePersistence, that specifically allows event listeners to be specified as objects, thus allowing Spring-instantiated bean to be injected. We then tell Spring's LocalContainerEntityManagerFactoryBean to use our special HibernatePersistence implementation, instead of the default HibernatePersistence class. Yes, this sidesteps the JPA-compliant interfaces, but I find no other way to accomplish the required injection of event listeners. The new Spring XML configuration looks like this:
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> ... <property name="persistenceProvider"> <bean class="org.hibernate.ejb.ConfigurableListenerBeansHibernatePersistence"> <property name="postLoadEventListeners"> <list> <bean ref="myPostLoadEventListener" /> </list> </property> </bean> </property> <property name="jpaPropertyMap"> <map> </map> </property> </bean>
Seems like Spring should provide a similar solution out-of-the-box.
Note that ConfigurableListenerBeansHibernatePersistence also takes it upon itself to maintain the default event listeners that Hibernate normally creates on its own (Ejb3*EventListeners and various Default*EventListeners), and appends the application-provided listeners to these. Contrast this with the default configuration behavior, which requires a developer to specify the Hibernate-provided event listeners in addition to the listeners being added by the application. (Hibernate's approach is the most flexible, but a pain for developers that simply want to add new listeners that do not alter the underlying behavior of Hibernate.)
Finally, note that current version of ConfigurableListenerBeansHibernatePersistence only provides configurability of the PostLoadEvent for now.
6 comments:
I need to investigate whether Spring's JpaVendorAdapator.postProcessEntityManagerFactory() method can be utilized in some way to achieve the above configuration. [http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/orm/jpa/JpaVendorAdapter.html]
It's strange because Steve Ebersole just told me that with an entity manager, all the JPA event listeners are added automatically to the session factory...
Actually i encountered the opposite problem: how to make @PostLoad work when you use "plain old hibernate"
which one of jar you get this
org.hibernate.ejb.ConfigurableListenerBeansHibernatePersistence
Can you give me link repository and dependency for maven?
which one of jar you get this
org.hibernate.ejb.ConfigurableListenerBeansHibernatePersistence
Can you give me link repository and dependency for maven?
@Kvic Kaizen
It's not in a jar, he has written it himself, but incorrectly used hibernates package space. Click on the name of it in the blog he links to his SCM
https://github.com/carmanconsulting/hibiscus
Post a Comment