Tuesday, September 11, 2007

Multiple Persistence Units with Spring, JPA, Tomcat

I was struggling with using Spring and JPA on Tomcat some time ago, but then could not manage and turned to EJB3 and Glassfish for my latest project.

EJB3 is far better than before, but it is not very easy to manage an application server and an "Enterprise Application". I had 3 projects in Eclipse: The enterprise application project, EJB and web application projects. Also, managing an application server is not as easy as managing Tomcat, considering the small size of my team (2 developers). Development - deployment - testing cycle is not easy, as we have to re-publish our application after every small change to JSP files or Java classes. EJB3 and Glassfish (or any other appserver) is also a powerful combination, but I was missing Spring and Tomcat.

We were half way through our development, and I decided to give another try to using Spring+JPA under Tomcat. This time, I found a blog in which this setup was described. Applying those steps and making some changes, I was successful to make it work and converted the application to Spring+JPA+Tomcat 6 in the weekend! We have more than one persistence units defined. Now we feel much confident that we will finish the project on time.

Here is what I did:

- In src/META-INF/persistence.xml define the persistence units:
<persistence-unit name="pu1" type="RESOURCE_LOCAL">

....
</persistence-unit>


<persistence-unit name="pu2" type="RESOURCE_LOCAL">
....

</persistence-unit>


- In META-INF folder (under the web application root, not the one in src folder which goes under WEB-INF/classes), create a file called context.xml. The "Loader" definition is necessary for Spring+JPA under Tomcat, and the "Resource" elements define JNDI entries:

<Context>
<Loader loaderClass="org.springframework.instrument.classloading.tomcat.TomcatInstrumentableClassLoader"
useSystemClassLoaderAsParent="false" />


<Resource name="jdbc/db1" auth="Container" type="javax.sql.DataSource"
maxActive="10" maxIdle="5" maxWait="15000"
removeAbandoned="true" removeAbandonedTimeout="300" logAbandoned="true"
initialSize="2" minIdle="2" username="username1" password="password1"
driverClassName="oracle.jdbc.driver.OracleDriver"
url="jdbc:oracle:thin:@localhost:1521:xe"/>


<Resource name="jdbc/db2" auth="Container" type="javax.sql.DataSource"
maxActive="10" maxIdle="5" maxWait="15000"
removeAbandoned="true" removeAbandonedTimeout="300" logAbandoned="true"
initialSize="2" minIdle="2" username="username2" password="password2"
driverClassName="oracle.jdbc.driver.OracleDriver"
url="jdbc:oracle:thin:@localhost:1521:xe"/>


</Context>



- In web.xml, define "resource-ref" entries for db connections:
<resource-ref>
<description>DB Connection 1</description>
<res-ref-name>jdbc/db1</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>



<resource-ref>
<description>DB Connection 2</description>
<res-ref-name>jdbc/db2</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>

- Spring ContextLoaderListener must also be present in web.xml:

<listener>
<listener-class> org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>

- Beans in Spring application context look like:

<!-- JPA annotations bean post processor -->
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor">

<!-- Exception translation bean post processor -->
<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor">

<tx:annotation-driven manager="transactionManager1">
<tx:annotation-driven manager="transactionManager2">



<bean id="dao1" class="dao.DAO1JPA">
</bean>


<bean id="dao2" class="dao.DAO2JPA">
</bean>


<bean id="service1" class="service.Service1Impl">
<property name="dao" ref="dao1">
</bean>


<bean id="service2" class="service.Service2Impl">
<property name="dao" ref="dao2">
</bean>


<bean id="entityManagerFactory1" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="pu1">
<property name="dataSource" ref="dataSource1">
<property name="jpaVendorAdapter">
....
<property name="loadTimeWeaver">
<bean class="org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver">
</property>
</bean>


<bean id="entityManagerFactory2" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="pu2">
<property name="dataSource" ref="dataSource2">
<property name="jpaVendorAdapter">
....
<property name="loadTimeWeaver">
<bean class="org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver">
</property>
</bean>


<bean id="dataSource1" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:comp/env/jdbc/db1">
</bean>


<bean id="dataSource2" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:comp/env/jdbc/db2">
</bean>


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


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

- Let Spring inject EntityManager instances into DAO classes:
@PersistenceContext(unitName = "pu1")

private EntityManager emPU1;



- Database jars, spring-tomcat-weaver.jar and toplink-essentials.jar are placed in Tomcat/lib folder. Spring jars (spring.jar, spring-aspects.jar, spring-mock.jar) are placed in WEB-INF/lib.

- In Tomcat6/lib folder, annotations-api.jar contains classes which causes exceptions. From that jar, delete package "javax.persistence" to get rid of those exceptions. (Found this info here)

With this setup, develop - deploy - test cycle is much easier now. We also have access to all Spring goodies now: Easy e-mail sending, defining jobs with Quartz, JMX exporter, ....

(Code and XML formatting is not easy here, or I don't know how to format better on blogger. Any suggestions?)

10 comments:

Rakesh Aimreddy said...

Interesting blog entry, I was trying to looking for some thing exactly like this. But I was stucked, with the transactions. Meaning, you declared two transaction right. Now how do you use these two different type of transactions in your service layer.
For example we can use the annotation
@Transactional
How do we specifically say which transaction to use? or it does not matter (which I do not think so).

Turgay Zengin said...

Hi Rakesh,
Can you please be more specific, what are the two different types of transactions you mention?
@Transaction annotation is explained in Spring reference documentation
For example, when you annotate a method with @Transactional(propagation = Propagation.REQUIRES_NEW)
then Spring will start and finish a separate transaction for this method regardless of if a current transaction is present or not. However if you annotate with @Transactional(propagation = Propagation.REQUIRED)
then Spring will make sure that this method uses the current transaction if present, if not it does not create one.
HTH

Rakesh Aimreddy said...

Sorry about the confusion, what I meant was:

we have these transaction declared right:
tx:annotation-driven manager="transactionManager1"
tx:annotation-driven manager="transactionManager2"

And now when you use the annotation (@Transactional) on the service impl's then Spring will make sure that the methods in that service impl are wrapped around the transaction. basically underneath, what exactly happening, spring should be using one the transactionManager we declared to grab the transaction righ. So which transaction manager it will use, so that's my mystery.

I will appreciate if you could solve this, I don't know if I misunderstood the whole transaction management or what?

Turgay Zengin said...

I see what you mean.
I have not needed to use more than one persistence units in a service implementation, but I guess Spring should be handling this issue.
When you use 2 persistence units in a class, and a method is marked as @Transactional, Spring should be handling this using distributing transactions(XA). However I have never verified this.

Anonymous said...

Use Spring AOP instead of @Transactional using the tags

tx:advise
aop:config

But beware that Spring uses dynamic proxies NOT static proxies to crosscut transaction management code.

ideyatech said...

i have an article regarding spring transactions on JPA
http://www.ideyatech.com/2008/09/troubleshooting-tips-spring-transactions-on-jpa/

Anonymous said...

No unique bean of type [javax.persistence.EntityManagerFactory] is defined: expected single bean but found 2

Anonymous said...

No unique bean of type [javax.persistence.EntityManagerFactory] is defined: expected single bean but found 2

Rafael Ferreira said...

I was going in your approach but I found a simple solution in: http://static.springsource.org/spring/docs/2.5.x/reference/orm.html#orm-jpa-multiple-pu

Works for me!

Another solution!

cheers and good blog post!

http://rafacastanho.wordpress.com/

Anonymous said...

This might be of intrest for guys who want to use JTOM for transaction along with multiple persistence units.