How to setup multiple data sources with Spring and JPA


Codever Logo

(P) Codever is an open source bookmarks and snippets manager for developers & co. See our How To guides to help you get started. Public bookmarks repos on Github ⭐🙏


In this post I will show you how to setup two or more data sources in a Spring application where the access to the database is done via JPA. It will be a XML-based Spring configuration. To highlight the setup I will use a showcase that builds on an existing demo example I have committed on GitHub, that covers other two posts of mine

Don’t worry! You don’t have to understand what’s going on in those if you just want to see how the setup for multiple data sources looks like – I’ll do a quick introduction in the first part of the post.

1. Showcase

The demo application used in the posts mentioned presents how to use a REST API to execute CRUD operations against a back-end db, delivering podcasts. For the demo’s sake I’ll say a client of the REST API also needs resources(podcasts) from a “legacy” system – she needs to combine them with the ones from the actual system and present them to their users.

For that I will implement two new read operations that will GET the podcast(s) from the “legacy” system. The new REST facade layer – PodcastLegacyRestService – that delivers “legacy” resources will use the same data access layer to highlight the use of multiple data sources.

Now let’s see how to configure and code multiple data sources with Spring and JPA:

2. Configuration

2.1. Persistence.xml

The first thing I did was to modify the persistence.xml file by adding a new persistence unit that will correspond to the “legacy” entityManager, managing the new “legacy” data source:

<persistence version="2.0" xmlns="https://java.sun.com/xml/ns/persistence" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://java.sun.com/xml/ns/persistence https://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
   <persistence-unit name="demoRestPersistence">
      <provider>org.hibernate.ejb.HibernatePersistence</provider>
   </persistence-unit>

   <persistence-unit name="demoRestPersistenceLegacy">
      <provider>org.hibernate.ejb.HibernatePersistence</provider>
   </persistence-unit>
</persistence>

Note: The persistence unit defines a set of all entity classes that are managed by EntityManager instances in an application. This set of entity classes represents the data contained within a single data store.

Because I am using Spring you can see the configuration of the persistence unit in the persistence.xml is very lean. The actual configuration of the entity managers takes places in the Spring’s application context of the application. See the next section for the details.

2.2. Spring Appplication Context

In the Spring application context I just added new beans for the entity manager, transaction manger and datasource:

<beans xmlns="https://www.springframework.org/schema/beans"
	xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="https://www.springframework.org/schema/context"
	xmlns:tx="https://www.springframework.org/schema/tx"
	xsi:schemaLocation="
		https://www.springframework.org/schema/beans
		https://www.springframework.org/schema/beans/spring-beans.xsd

		https://www.springframework.org/schema/tx
		https://www.springframework.org/schema/tx/spring-tx.xsd

		https://www.springframework.org/schema/context
		https://www.springframework.org/schema/context/spring-context.xsd">

	<context:component-scan base-package="org.codingpedia.demo.rest.*" />

	<!-- ************ JPA configuration *********** -->
	<tx:annotation-driven transaction-manager="transactionManager" />
    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory" />
    </bean>
    <bean id="transactionManagerLegacy" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactoryLegacy" />
    </bean>
    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="persistenceXmlLocation" value="classpath:config/persistence-demo.xml" />
        <property name="persistenceUnitName" value="demoRestPersistence" />
        <property name="dataSource" ref="restDemoDS" />
        <property name="packagesToScan" value="org.codingpedia.demo.*" />
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <property name="showSql" value="true" />
                <property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect" />
            </bean>
        </property>
    </bean>
    <bean id="entityManagerFactoryLegacy" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="persistenceXmlLocation" value="classpath:config/persistence-demo.xml" />
        <property name="persistenceUnitName" value="demoRestPersistenceLegacy" />
        <property name="dataSource" ref="restDemoLegacyDS" />
        <property name="packagesToScan" value="org.codingpedia.demo.*" />
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <property name="showSql" value="true" />
                <property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect" />
            </bean>
        </property>
    </bean>

	<bean id="podcastDao" class="org.codingpedia.demo.rest.dao.impl.PodcastDaoJPA2Impl"/>
    <bean id="podcastRestService" class="org.codingpedia.demo.rest.service.PodcastRestService" />
    <bean id="podcastLegacyRestService" class="org.codingpedia.demo.rest.service.PodcastLegacyRestService" />

	<bean id="restDemoDS" class="org.springframework.jndi.JndiObjectFactoryBean" scope="singleton">
	    <property name="jndiName" value="java:comp/env/jdbc/restDemoDB" />
	    <property name="resourceRef" value="true" />
	</bean>
	<bean id="restDemoLegacyDS" class="org.springframework.jndi.JndiObjectFactoryBean" scope="singleton">
	    <property name="jndiName" value="java:comp/env/jdbc/restDemoLegacyDB" />
	    <property name="resourceRef" value="true" />
	</bean>
</beans>

Note the persistenceUnitName property of the entityManagerFactory is pointing to the corresponding persistence unit.

Here’s a quick recap of the main configured beans:

  • entityManagerFactoryLegacy (org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean) is a org.springframework.beans.factory.FactoryBean that creates a JPA javax.persistence.EntityManagerFactory according to JPA’s standard container bootstrap contract. This is the most powerful way to set up a shared JPA EntityManagerFactory in a Spring application context; the EntityManagerFactory can then be passed to JPA-based DAOs via dependency injection. Note that switching to a JNDI lookup or to a LocalEntityManagerFactoryBean definition is just a matter of
    configuration!
    As with LocalEntityManagerFactoryBean, configuration settings are usually read in from a META-INF/persistence.xml config file, residing in the class path, according to the general JPA configuration contract. However, this FactoryBean is more flexible in that you can override the location of the persistence.xml file (as here the case), specify the JDBC DataSources to link to, etc. Furthermore, it allows for pluggable class instrumentation through Spring’s org.springframework.instrument.classloading.LoadTimeWeaver abstraction, instead of being tied to a special VM agent specified on JVM startup.
  • transactionManagerLegacy (org.springframework.orm.jpa.JpaTransactionManager) is a org.springframework.transaction.PlatformTransactionManager implementation for a single JPA javax.persistence.EntityManagerFactory. Binds a JPA EntityManager from the specified factory to the thread, potentially allowing for one thread-bound EntityManager per factory. SharedEntityManagerCreator and JpaTemplate are aware of thread-bound entity managers and participate in such transactions automatically. Using either is required for JPA access code supporting this transaction management mechanism.
    This transaction manager is appropriate for applications that use a single JPA EntityManagerFactory for transactional data access. JTA (usually through org.springframework.transaction.jta.JtaTransactionManager) is necessary for accessing multiple transactional resources within the same transaction. Note that you need to configure your JPA provider accordingly in order to make it participate in JTA transactions.
  • 2.3. In code

    As mentioned before to highlight the multiple data source configuration in code I extended the DAO layer class with methods to access the “legacy” system:

    package org.codingpedia.demo.rest.dao.impl;
    
    import java.util.Calendar;
    import java.util.Date;
    import java.util.GregorianCalendar;
    import java.util.List;
    import java.util.TimeZone;
    
    import javax.persistence.EntityManager;
    import javax.persistence.NoResultException;
    import javax.persistence.PersistenceContext;
    import javax.persistence.Query;
    import javax.persistence.TemporalType;
    import javax.persistence.TypedQuery;
    
    import org.codingpedia.demo.rest.dao.PodcastDao;
    import org.codingpedia.demo.rest.entities.Podcast;
    
    public class PodcastDaoJPA2Impl implements PodcastDao {
    
    	@PersistenceContext(unitName="demoRestPersistence")
    	private EntityManager entityManager;
    
    	@PersistenceContext(unitName="demoRestPersistenceLegacy")
    	private EntityManager entityManagerLegacy;
    
    	public List<Podcast> getPodcasts() {
    
    		String qlString = "SELECT p FROM Podcast p";
    		TypedQuery<Podcast> query = entityManager.createQuery(qlString, Podcast.class);
    
    		return query.getResultList();
    	}
    	/* .............................*/
    	public List<Podcast> getLegacyPodcasts() {
    
    		String qlString = "SELECT p FROM Podcast p";
    		TypedQuery<Podcast> query = entityManagerLegacy.createQuery(qlString, Podcast.class);
    
    		return query.getResultList();
    	}
    
    }

    You see here how you can use multiple entity managers in the same class. And we that we conclude the configuration example of accessing multiple data sources.

    Code Tip – you can best see the differences to the code with one data source configuration by having a look at my GitHub commit for this post at https://github.com/adi-matei/demo-restWS-spring-jersey-jpa2-hibernate/commit/f37d52d88348eb125097a561e9105d61fffa08c3

    2.3.1. Using @Transactional

    If you are using transactions via the @Transactional annotation you need to specify the transaction manager you are using:

    /********************* Create related methods implementation ***********************/
    @Transactional("transactionManager")
    public Long createPodcast(Podcast podcast) throws AppException {
    
    	validateInputForCreation(podcast);
    
    	//verify existence of resource in the db (feed must be unique)
    	PodcastEntity podcastByFeed = podcastDao.getPodcastByFeed(podcast.getFeed());
    	if(podcastByFeed != null){
    		throw new AppException(Response.Status.CONFLICT.getStatusCode(), 409, "Podcast with feed already existing in the database with the id " + podcastByFeed.getId(),
    				"Please verify that the feed and title are properly generated", AppConstants.BLOG_POST_URL);
    	}
    
    	return podcastDao.createPodcast(new PodcastEntity(podcast));
    }

    3. Testing it – optional

    To test the new “legacy” functionality you can start the jetty server configured in the pom.xml, file by issuing the following maven command in the root directory of the project:

    mvn jetty:run -Djetty.port=8888

    Once Jetty is started execute GET operations on the following URIs:

    to get all podcasts, or respectively a specific podcast from the “legacy” system.

    You can also have look at my video on how to test a REST API with the DEV HTTP Client

    Well, that’s it. You’ve learned how to configure multiple a Spring application to access multiple data sources via JPA.

    If you’ve found it useful, please help it spread by sharing it on Twitter, Google+ or Facebook. Thank you! Don’t forget also to check out Podcastpedia.org – you’ll find for sure interesting podcasts and episodes. We are grateful for your support.

    4. Resources

    4.1. Codingpedia

    4.2. GitHub

    4.3. Web

    Subscribe to our newsletter for more code resources and news

    Adrian Matei (aka adixchen)

    Adrian Matei (aka adixchen)
    Life force expressing itself as a coding capable human being

    routerLink with query params in Angular html template

    routerLink with query params in Angular html template code snippet Continue reading