Spring Boot + Multiple Datasources + JPA (2024)
In this tutorial, will integrate multiple data sources in a Spring Boot application that uses Spring Data JPA.
In some scenarios, we may need to save data in two separate datasources or databases at the same time. In that case, we want JPA to identify and save the data in their respective datasources. We can use the steps outlined here to achieve and overcome this problem.
Take a look at our suggested posts:
Now, let's create Spring Boot application from Spring Initializr.
Project Structure
Maven Dependency
Add below dependencies for JPA and MYSQL database.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.techgeeknext</groupId>
<artifactId>SpringBoot-Multiple-Datasources</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>SpringBoot-Multiple-Datasources</name>
<description>Spring Boot Multiple Datasources Example</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Datasources Connection Properties
Instead of using the usual JPA database connection URL, we use additional identifiers such as company
and employee
when configuring database connection properties in this property file.
spring.datasource.company.url=jdbc:mysql://localhost/techgeeknext_company_db?createDatabaseIfNotExist=true&autoReconnect=true&useSSL=false&allowPublicKeyRetrieval=true
spring.datasource.company.username=root
spring.datasource.company.password=root
spring.datasource.company.platform=mysql
spring.datasource.employee.url=jdbc:mysql://localhost/techgeeknext_employee_db?createDatabaseIfNotExist=true&autoReconnect=true&useSSL=false&allowPublicKeyRetrieval=true
spring.datasource.employee.username=root
spring.datasource.employee.password=root
spring.datasource.employee.platform=mysql
spring.jpa.hibernate.ddl-auto=update
spring.jpa.generate-ddl=true
spring.jpa.show-sql=true
Configuration class for Multiple Datasources
Company Datasource Configuration
The @EnableJpaRepositories annotation is used to provide a package where this datasource should access repositories in order to access the company's database.
package com.techgeeknext.configuration;
import com.techgeeknext.entities.company.Company;
import com.zaxxer.hikari.HikariDataSource;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;
import javax.sql.DataSource;
@Configuration
@EnableJpaRepositories(basePackages = "com.techgeeknext.repository.company",
entityManagerFactoryRef = "companyEntityManagerFactory",
transactionManagerRef = "companyTransactionManager")
public class CompanyDataSourceConfiguration {
/**
* Here it will get url, username, password and driver-class-name
* which we have defined in application properties file for company.
* @return
*/
@Bean
@ConfigurationProperties("spring.datasource.company")
public DataSourceProperties companyDataSourceProperties() {
return new DataSourceProperties();
}
/**
* Create the datasource using companyDataSourceProperties
* @return
*/
@Bean
@ConfigurationProperties("spring.datasource.company.configuration")
public DataSource companyDataSource() {
return companyDataSourceProperties().initializeDataSourceBuilder()
.type(HikariDataSource.class).build();
}
/**
* EntityManager will find Entity classes inside this company package
* (i.e com.techgeeknext.entities.company.Company).
* @param builder
* @return
*/
@Bean(name = "companyEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean companyEntityManagerFactory(
EntityManagerFactoryBuilder builder) {
return builder
.dataSource(companyDataSource())
.packages(Company.class)
.build();
}
@Bean
public PlatformTransactionManager companyTransactionManager(
final @Qualifier("companyEntityManagerFactory") LocalContainerEntityManagerFactoryBean companyEntityManagerFactory) {
return new JpaTransactionManager(companyEntityManagerFactory.getObject());
}
}
Employee Datasource Configuration
package com.techgeeknext.configuration;
import com.techgeeknext.entities.employee.Employee;
import com.zaxxer.hikari.HikariDataSource;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(basePackages = "com.techgeeknext.repository.employee",
entityManagerFactoryRef = "employeeEntityManagerFactory",
transactionManagerRef= "employeeTransactionManager")
public class EmployeeDataSourceConfiguration {
/**
* Here it will get url, username, password and driver-class-name
* which we have defined in application properties file for employee.
* @return
*/
@Bean
@Primary
@ConfigurationProperties("spring.datasource.employee")
public DataSourceProperties employeeDatasourceProperties() {
return new DataSourceProperties();
}
/**
* Create the datasource using employeeDatasourceProperties
* @return
*/
@Bean
@Primary
@ConfigurationProperties("spring.datasource.employee.configuration")
public DataSource employeeDataSource() {
return employeeDatasourceProperties().initializeDataSourceBuilder()
.type(HikariDataSource.class).build();
}
/**
* EntityManager will find Entity classes inside this company package
* (i.e com.techgeeknext.entities.employee.Employee).
* @param builder
* @return
*/
@Primary
@Bean(name = "employeeEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean employeeEntityManagerFactory(EntityManagerFactoryBuilder builder) {
return builder
.dataSource(employeeDataSource())
// for specifying package .packages("com.techgeeknext.entities.employee.type")
.packages(Employee.class)
.build();
}
@Primary
@Bean
public PlatformTransactionManager employeeTransactionManager(
final @Qualifier("employeeEntityManagerFactory") LocalContainerEntityManagerFactoryBean employeeEntityManagerFactory) {
return new JpaTransactionManager(employeeEntityManagerFactory.getObject());
}
}
JPA Repository to access/save multiple datasource
Company Repository
package com.techgeeknext.repository.company;
import com.techgeeknext.entities.company.Company;
import org.springframework.data.repository.CrudRepository;
public interface CompanyRepository extends CrudRepository<Company, Integer> {
}
Employee Repository
package com.techgeeknext.repository.employee;
import com.techgeeknext.entities.employee.Employee;
import org.springframework.data.repository.CrudRepository;
public interface EmployeeRepository extends CrudRepository<Employee, Integer> {
}
Test
Run the Spring Boot application using mvn spring-boot:run
.
When the application starts, you'll notice two datasources will be created and respective tables inside each datasources as shown below:
Download Source Code
The full source code for this article can be found on below.Download it here - Spring Boot + Multiple Datasources + JPA Example