Monday, August 10, 2009

How to use GlassFish managed JPA EntityManagers in Spring

When you deploy your application into a Java EE 5 application server it detects the persistence.xml, creates an EntityManager for each persistence unit, and exposes them in JNDI. You can get Spring to load the EntityManagers of all your persistence units from JNDI. First you need to tell web.xml that you want to load the persistence unit references from JNDI. The JNDI name always starts with "persistence/" and ends with the persistence unit name. For example:
 <persistence-unit-ref> 
<persistence-unit-ref-name>persistence/MyPU1</persistence-unit-ref-name> 
<persistence-unit-name>MyPU1</persistence-unit-name> 
</persistence-unit-ref> 

<persistence-unit-ref> 
<persistence-unit-ref-name>persistence/MyPU2</persistence-unit-ref-name> 
<persistence-unit-name>MyPU2</persistence-unit-name> 
</persistence-unit-ref> 
Next, add the following configuration to your Spring XML config file:
 <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" > 
<property name="persistenceUnits"> 
<map> 
<entry key="MyPU1" value="persistence/MyPU1"/> 
<entry key="MyPU2" value="persistence/MyPU2"/> 
</map> 
</property> 
</bean> 

<tx:jta-transaction-manager/> 
<tx:annotation-driven/> 
Spring is now configured to use the JPA EntityManagers and JTA transactions from GlassFish. Use persistence.xml, @PersistenceUnit, and @PersistenceContext like normal. Use Spring's proprietary @Transactional annotation for transaction demarcation. If you need to query both persistence units within the same transaction then you need to use XA data sources, or mark code that uses the second persistence unit with @Transactional(propagation=Propagation.REQUIRES_NEW).

9 comments:

Anonymous said...

Hello Ryan.

Thanks for this entry. I've a project with this configuration (mmm..sorry for my english)...

In persistence file, JTA is defined with jta-data-source...

I include in Spring config file the lines that you says, but when I start, I always get this error:

I have read 1000 post in 1000 forum, but cannot make this work...

"When I start, I always get this error:
org.hibernate.HibernateException: The chose transaction estrategy requires access to the JTA transaction manager.."

Thanks. JGP.

Ryan de Laplante said...

Since you are using Hibernate instead of the default JPA provider, you need to tell Hibernate what transaction manager to use. In your persistence.xml, make sure Hibernate has this property:

<property name="hibernate.transaction.manager_lookup_class" value="org.hibernate.transaction.SunONETransactionManagerLookup"/>

Anonymous said...

Thank you very much, really.

In every forum I read, I was given many solutions to change Spring File Config, but none worked for me. I was desperate

Do not know how much you have helped me.

Thanks again from Spain. Jes.G.P.

Ryan de Laplante said...

Great, I am happy to have been able to help you. I think I do understand how much I helped you because I spent weeks screwing around with Spring XML config files and in forums trying to get JPA + JTA working before I found the solution in this blog entry. That experience made me realize how much simpler Java EE can be, because everything works out of the box -- no integrations necessary. SpringSource's documentation assumes you are running in Tomcat and doesn't help in scenarios like this one.

Anonymous said...

I agree with you about Spring's doc.

I've always been working with EJB and I begin with Spring.

In these few days, I feel that to give a single step means 'more effort and risk that goes wrong'.

Maybe it's just the beginning and then see more clearly :P:P

Thanks again.
Jes. G.P.

Matthew said...

Do you have a complete project we can download to see how this all works?

seshu said...

hi Ryan
i have configured my application same like the above mentioned but when i am running the application i am get an error say No bean named 'EVoucherPU' i am using two persistence units named BackOfficePU and EVoucherPU when i remove BackOfficePU or EVoucherPU it runs fine. ie only on single persistence it is running. please help me to overcome this issue Thanks seshu

Ryan de Laplante said...

Are you using GlassFish? Did you configure Spring to know about both persistence units? In my example they were called MyPU1 and MyPU2. Also, in your code are you specifying the persistence unit name like this: @PersistenceContext(unitName = "EVoucherPU") I'm not sure what else to ask because the above configuration works for me on several projects.

@Mathew: sorry I don't have a sample project. It was copied from a closed source project and modified for this blog entry.

seshu said...

thank u i got. i have not configure the EVoucherPU in the spring thank u for u reply and thank u for this post it helped me a lot ones again thank u.

Post a Comment