Thursday, March 24, 2011

How to add another data source in JPA

It's quite easy to create a data source using JPA support of Spring framework. It not so difficult to add another data source to your application as well.

In META-INF/persistence.xml, define another persistence unit.

     <persistence-unit name="anotherUnit" transaction-type="RESOURCE_LOCAL">  
         <class>com.youcompany.YourClass</class>  
         <exclude-unlisted-classes>true</exclude-unlisted-classes>  
         <properties>  
             <property name="hibernate.hbm2ddl.auto" value="update" />  
             <!-- validate | update | create | create-drop -->  
         </properties>  
     </persistence-unit>  

Note that you should define all the domain classes you will be using in the defined persistence unit in <class> elements.

Define another database context xml file.
 <?xml version="1.0" encoding="UTF-8"?>  
 <beans xmlns="http://www.springframework.org/schema/beans"  
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"  
     xmlns:tx="http://www.springframework.org/schema/tx" xmlns:p="http://www.springframework.org/schema/p"  
     xmlns:aop="http://www.springframework.org/schema/aop"  
     xsi:schemaLocation="http://www.springframework.org/schema/beans 
     http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
     http://www.springframework.org/schema/context
     http://www.springframework.org/schema/context/spring-context-3.0.xsd
     http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
     http://www.springframework.org/schema/aop
     http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">  
     <!-- holding properties for database connectivity / -->  
     <context:property-placeholder location="classpath:config.properties" />  
     <bean id="anotherDataSource" class="org.apache.commons.dbcp.BasicDataSource"  
         destroy-method="close">  
         <property name="driverClassName" value="${db.driver}" />  
         <property name="url" value="${db.url}" />  
         <property name="username" value="${db.user}" />  
         <property name="password" value="${db.pass}" />  
         <property name="validationQuery" value="${dbcp.validationQuery}" />  
         <property name="testWhileIdle" value="${dbcp.testWhileIdle}" />  
         <property name="timeBetweenEvictionRunsMillis" value="${dbcp.timeBetweenEvictionRunsMillis}" />  
         <property name="numTestsPerEvictionRun" value="${dbcp.numTestsPerEvictionRun}" />  
         <property name="minEvictableIdleTimeMillis" value="${dbcp.minEvictableIdleTimeMillis}" />  
     </bean>  
     <bean id="anotherJpaAdapter"  
         class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"  
         p:database="${db.database}" p:showSql="${db.showSql}" />  
     <bean id="anotherEntityManagerFactory"  
         class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"  
         p:dataSource-ref="anotherDataSource" p:jpaVendorAdapter-ref="anotherJpaAdapter">  
         <property name="loadTimeWeaver">  
             <bean  
                 class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver" />  
         </property>  
         <property name="persistenceUnitName" value="anotherUnit"></property>  
     </bean>  
     <bean id="anotherTxManager" class="org.springframework.orm.jpa.JpaTransactionManager"  
         p:entityManagerFactory-ref="anotherEntityManagerFactory" />  
 </beans>  

In the JPA implementation of generic DAO class, annotate the 1st persistence unit in the setter of EntityManager.

     protected EntityManager entityManager;  
     @PersistenceContext(unitName="firstUnit")  
     public void setEntityManager(EntityManager entityManager) {  
         this.entityManager = entityManager;  
     }  

Create another generic DAO class for new persistence unit. All the operations to the new domain objects should be accomplished via this new generic DAO.

If you want to access data source directly, use
     @Autowired  
     @Qualifier("anotherDataSource")  
     private DataSource dataSource;  

Don't forget to getAutoCommit and keep the status of any connection you get from data source if you need to setAutoCommit yourself, and close the connection in finally statement.

That's it.