Using Spring defined transactionManager in JPA/Hibernate
Asked Answered
S

3

11

Suppose you use JPA with Spring, with Hibernate as JPA implementation. JPA transaction mode is "JTA", so you need to pass the container transactionManager to Hibernate. The classical answer is to set hibernate.transaction.manager_lookup_class to the matching class for your server.

However, I think it's a shame to have this depend of server specific configuration as you already found the transactionManager in Spring with <tx:jta-transaction-manager>.

Is there a way to give this transactionManager to Hibernate with a configuration like

 <bean id="entityManagerFactory"
  class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
  <property name="persistenceUnitName" value="persistence_unit_name"/>
  <property name="jpaVendorAdapter">
   <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"/>
  </property>
  <property name="jpaProperties">
   <props>
    <prop key="hibernate.transaction.manager_lookup_class">
     org.hibernate.transaction.SunONETransactionManagerLookup
    </prop>
   </props>
  </property>
 </bean>

 <tx:jta-transaction-manager/>

The goal is to get rid of the org.hibernate.transaction.SunONETransactionManagerLookup property. By the way, I really have two different server implementations in mind.

EDIT : without the transaction manager configuration, Hibernate chokes when creating the EntityManagerFactory :

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in URL [file:/C:/configuration/afoCuad-metier-ear/entitymanager-base-context.xml]: Invocation of init method failed; nested exception is javax.persistence.PersistenceException: [PersistenceUnit: fr.tm.ima.cuad-afoCuad-metier-ejb-PU] Unable to build EntityManagerFactory
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1420)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:519)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:456)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:291)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:288)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:190)
at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor.findDefaultEntityManagerFactory(PersistenceAnnotationBeanPostProcessor.java:529)
at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor.findEntityManagerFactory(PersistenceAnnotationBeanPostProcessor.java:495)
at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor$PersistenceElement.resolveEntityManager(PersistenceAnnotationBeanPostProcessor.java:656)
at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor$PersistenceElement.getResourceToInject(PersistenceAnnotationBeanPostProcessor.java:629)
at org.springframework.beans.factory.annotation.InjectionMetadata$InjectedElement.inject(InjectionMetadata.java:147)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:84)
at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor.postProcessPropertyValues(PersistenceAnnotationBeanPostProcessor.java:338)
... 80 more
Caused by: javax.persistence.PersistenceException: [PersistenceUnit: fr.tm.ima.cuad-afoCuad-metier-ejb-PU] Unable to build EntityManagerFactory
at org.hibernate.ejb.Ejb3Configuration.buildEntityManagerFactory(Ejb3Configuration.java:901)
at org.hibernate.ejb.HibernatePersistence.createContainerEntityManagerFactory(HibernatePersistence.java:74)
at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:225)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:308)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1477)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1417)
... 93 more
Caused by: org.hibernate.HibernateException: The chosen transaction strategy requires access to the JTA TransactionManager
at org.hibernate.impl.SessionFactoryImpl.<init>(SessionFactoryImpl.java:401)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1385)
at org.hibernate.cfg.AnnotationConfiguration.buildSessionFactory(AnnotationConfiguration.java:954)
at org.hibernate.ejb.Ejb3Configuration.buildEntityManagerFactory(Ejb3Configuration.java:892)
... 98 more
Selfidentity answered 29/9, 2010 at 8:43 Comment(0)
F
1

Unfortunately if one looks at the Hibernate APIs like many other JBoss products they have a a class typically called Configuration to hold most if not all the main config stuff. Unfortunately they (JBoss) seem to like to hold "Strings" for parameters and class to locate instances. Almost always its often impossible to simply set an actual premade ready to go setup.

I am about to try something similar to the following for the very same reason you are mentioning.

  • Create an implementation of TransactionManagerLookup
  • include a setter which takes a TM and sets a thread local variable + instance.
  • pass the name of TML inside the properties you pass to the Configuration.
  • When Your TML startsup copy the thread local variable to your instance fie.d.
  • clear the threadlocal once everything is done.
Frigorific answered 30/1, 2011 at 23:54 Comment(1)
OK, thanks, I suppose the right anwser would be to open a bug for Hibernate, as it's a the configurability on their side that is at fault.Selfidentity
B
7

First of all - do you really need JTA? Typically spring+hibernate don't require it. You can use a simple JpaTransactionManager / HibernateTransactionManager.

If you really want JTA, then you will need a JTA provider. If not running in an application server, check this question for how to use JTA in a servlet container. (Also take a look at this question)

Finally, hibernate docs specify that, for container-managed transactions:

Declarative transaction demarcation is a standard feature of EJB, also known as container-managed transactions (CMT). In EJB 2.x you would use XML deployment descriptors to create your transaction assembly. In EJB 3.x you can use JDK 5.0 annotation metadata directly in your source code, a much less verbose approach. To enable CMT transaction demarcation for EJBs in Hibernate configuration:

  • set hibernate.transaction.manager_lookup_class to a lookup strategy for your JEE container
  • set hibernate.transaction.factory_class to org.hibernate.transaction.CMTTransactionFactory

The second point is perhaps something you've missed?

What the documentation says in addition to that (the next section) is that if you want declarative transaction, hibernate is not where you should look at. You'd need to create an interceptor. That's exactly what spring transaction managers are. And that would be my choice given your technology stack (Spring).

If you wish not to rely on a single JTA provider, then make two builds. For example maven has "maven profiles", which allow to make builds for different environments.

Boxfish answered 10/12, 2010 at 8:15 Comment(4)
Yes, JTA is required. I wonder why people always assume it's possible to answer a question by omitting one of the elements. For 'lookup_class' and 'factory_class', I didn't miss that part, but I must not be dependend on the JEE container that is used, the application is to be deployed on (at least) two different application servers.Selfidentity
@Selfidentity people with broader view on technology assume some things because of their experience. For example I've rarely seen a spring app with JTA, while I've seen many cases when people throw themselves into it without actually needing it. For the rest of your comment - see my update.Boxfish
why would anyone use the Spring TM layer... it does not do anything its just another layer with different method names and adds no new or better functionality over JTA. In the end its going to do a jndi lookup (if one uses the JTA impl) which ends up in the same problem of magic Strings and singletons.Frigorific
@mP - spring transaction management does not use JTA. It has its own transaction managers that do a pretty good job. Apart from that - it provides an abstraction over different transactional technologies. So you can annotate your objects and if you later switch the transaction management strategies, just change a few configurations.Boxfish
F
1

Unfortunately if one looks at the Hibernate APIs like many other JBoss products they have a a class typically called Configuration to hold most if not all the main config stuff. Unfortunately they (JBoss) seem to like to hold "Strings" for parameters and class to locate instances. Almost always its often impossible to simply set an actual premade ready to go setup.

I am about to try something similar to the following for the very same reason you are mentioning.

  • Create an implementation of TransactionManagerLookup
  • include a setter which takes a TM and sets a thread local variable + instance.
  • pass the name of TML inside the properties you pass to the Configuration.
  • When Your TML startsup copy the thread local variable to your instance fie.d.
  • clear the threadlocal once everything is done.
Frigorific answered 30/1, 2011 at 23:54 Comment(1)
OK, thanks, I suppose the right anwser would be to open a bug for Hibernate, as it's a the configurability on their side that is at fault.Selfidentity
S
0

I've recently been doing some stuff with JPA/Grails and the configuration I used was along these lines:

Does this help at all?

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="dataSource" ref="dataSource"/>
    <property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>

<bean id="entityManagerFactory" class="org.hibernate.ejb.EntityManagerFactoryImpl">
 <constructor-arg index="0" ref="sessionFactory"/>
 <constructor-arg index="1">
   <bean id="javax.persistence.spi.PersistenceUnitTransactionType.RESOURCE_LOCAL" class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean" />
 </constructor-arg>
 <constructor-arg index="2" value="true"/>
 <constructor-arg index="3"><null/></constructor-arg>
</bean>
Sclerodermatous answered 9/12, 2010 at 15:54 Comment(1)
Well, you can't really do the same thing with JTA.Selfidentity

© 2022 - 2024 — McMap. All rights reserved.