Using Multiple Databases with Spring Data JPA in Spring Boot
This tutorial will guide you through the process of using multiple databases with Spring Data JPA in a Spring Boot application. Whether you're dealing with different types of data or optimizing performance, leveraging multiple databases can be a powerful solution.
The guide covers the use of multiple databases, including SQL Server, MySQL, Oracle, and PostgreSQL, within a single Spring Boot project.
Follow the steps below to complete this tutorial:
Step 1: Add Dependencies
Start by adding the necessary database driver dependencies for SQL Server, MySQL, Oracle, and PostgreSQL. Customize these additions based on your specific project needs.
<!--For SQLServer-->
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>mssql-jdbc</artifactId>
<scope>runtime</scope>
</dependency>
<!--For MySQL-->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<!--For Oracle-->
<dependency>
<groupId>com.oracle.database.jdbc</groupId>
<artifactId>ojdbc8</artifactId>
<scope>runtime</scope>
</dependency>
<!--For PostgreSQL-->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<!--For Spring JPA-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!--For Lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
//For SQLServer
runtimeOnly 'com.microsoft.sqlserver:mssql-jdbc'
//For MySQL
runtimeOnly 'com.mysql:mysql-connector-j'
//For Oracle
runtimeOnly 'com.oracle.database.jdbc:ojdbc8'
//For PostgreSQL
runtimeOnly 'org.postgresql:postgresql'
//For Spring JPA
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
//For Lombok
annotationProcessor 'org.projectlombok:lombok'
Step 2: Add Database Configurations
Define database connection properties such as URL, username, password, and driver class for each database in your application.properties or application.yml file. For example:
server.port=8080
# MySQL Configuration database 1
spring.datasource.mysql.jdbc-url=jdbc:mysql://localhost:3306/my_mysql_db1
spring.datasource.mysql.username=root
spring.datasource.mysql.password=Testing123$
spring.datasource.mysql.driver-class-name=com.mysql.cj.jdbc.Driver
# MySQL Configuration database 2
spring.second-datasource.mysql.jdbc-url=jdbc:mysql://localhost:3306/my_mysql_db2
spring.second-datasource.mysql.username=root
spring.second-datasource.mysql.password=Testing123$
spring.second-datasource.mysql.driver-class-name=com.mysql.cj.jdbc.Driver
# PostgreSQL Configuration
spring.datasource.postgresql.jdbc-url=jdbc:postgresql://localhost:5432/your_database_name
spring.datasource.postgresql.username=your_username
spring.datasource.postgresql.password=your_password
spring.datasource.postgresql.driver-class-name=org.postgresql.Driver
# SqlServer Configuration
spring.datasource.sqlserver.jdbc-url=jdbc:sqlserver://localhost:1433;databaseName=your_database_name;encrypt=true;trustServerCertificate=true;integratedSecurity = false;
spring.datasource.sqlserver.username=your_user_name
spring.datasource.sqlserver.password=your_password
spring.datasource.sqlserver.driver-class-name=com.microsoft.sqlserver.jdbc.SQLServerDriver
# Oracle Configuration
spring.datasource.oracle.jdbc-url=jdbc:oracle:thin:@localhost:1521/your_oracle_SID
spring.datasource.oracle.username=your_username
spring.datasource.oracle.password=your_password
spring.datasource.oracle.driver-class-name=oracle.jdbc.OracleDriver
spring.jpa.generate-ddl=true
spring.jpa.hibernate.ddl-auto=create
Step 3: Define Data Source Configurations
You will need to configure data sources for each database in your application. Spring Boot provides a way to define multiple data source beans. Create a class named DataSourceConfig to define data source beans for each database (We must define at least one data source as a primary by annotating with @primary):
package com.example.app.config;
import javax.sql.DataSource;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
@Configuration
public class DataSourceConfig {
// Datasource bean for MySQL database 1
@Primary
@Bean
@ConfigurationProperties("spring.datasource.mysql")
public DataSource mysqlDb1DataSource() {
return DataSourceBuilder.create().build();
}
// Datasource bean for MySQL database 2
@Bean
@ConfigurationProperties("spring.second-datasource.mysql")
public DataSource mysqlDb2DataSource() {
return DataSourceBuilder.create().build();
}
// Datasource bean for Oracle
@Bean
@ConfigurationProperties("spring.datasource.oracle")
public DataSource oracleDb1DataSource() {
return DataSourceBuilder.create().build();
}
// Datasource bean for PostgreSQL database 1
@Bean
@ConfigurationProperties("spring.datasource.postgresql")
public DataSource postgresqlDb1DataSource() {
return DataSourceBuilder.create().build();
}
// Datasource bean for SQLServer
@Bean
@ConfigurationProperties("spring.datasource.sqlserver")
public DataSource sqlserverDb1DataSource() {
return DataSourceBuilder.create().build();
}
}
Step 4: Configure EntityManagerFactory and TransactionManager
Create separate classes to configure the EntityManagerFactory and TransactionManager for each database.
package com.example.app.config;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Qualifier;
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 jakarta.persistence.EntityManagerFactory;
@Configuration
@EnableJpaRepositories(basePackages = "com.example.app.product.repository",
entityManagerFactoryRef = "mysqlDb1EntityManagerFactory",
transactionManagerRef = "mysqlDb1TransactionManager")
public class MySqlDb1Config {
@Primary
@Bean(name = "mysqlDb1EntityManagerFactory")
public LocalContainerEntityManagerFactoryBean mysqlDb1EntityManagerFactory(
EntityManagerFactoryBuilder builder, @Qualifier("mysqlDb1DataSource") DataSource dataSource) {
return builder.dataSource(dataSource).packages(new String[] {"com.example.app.product.entity"})
.persistenceUnit("mysql").build();
}
@Primary
@Bean(name = "mysqlDb1TransactionManager")
public PlatformTransactionManager mysqlDb1TransactionManager(
@Qualifier("mysqlDb1EntityManagerFactory") EntityManagerFactory entityManagerFactory) {
return new JpaTransactionManager(entityManagerFactory);
}
}
package com.example.app.config;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Qualifier;
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 jakarta.persistence.EntityManagerFactory;
@Configuration
@EnableJpaRepositories(basePackages = "com.example.app.customer.repository",
entityManagerFactoryRef = "mysqlDb2EntityManagerFactory",
transactionManagerRef = "mysqlDb2TransactionManager")
public class MySqlDb2Config {
@Bean(name = "mysqlDb2EntityManagerFactory")
public LocalContainerEntityManagerFactoryBean mysqlDb2EntityManagerFactory(
EntityManagerFactoryBuilder builder, @Qualifier("mysqlDb2DataSource") DataSource dataSource) {
return builder.dataSource(dataSource).packages(new String[] {"com.example.app.customer.entity"})
.persistenceUnit("mysql").build();
}
@Bean(name = "mysqlDb2TransactionManager")
public PlatformTransactionManager mysqlDb2TransactionManager(
@Qualifier("mysqlDb2EntityManagerFactory") EntityManagerFactory entityManagerFactory) {
return new JpaTransactionManager(entityManagerFactory);
}
}
package com.example.app.config;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Qualifier;
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 jakarta.persistence.EntityManagerFactory;
@Configuration
@EnableJpaRepositories(basePackages = "com.example.app.transaction.repository",
entityManagerFactoryRef = "postgresqlDb1EntityManagerFactory",
transactionManagerRef = "postgresqlDb1TransactionManager")
public class PostgresqlDb1Config {
@Bean(name = "postgresqlDb1EntityManagerFactory")
public LocalContainerEntityManagerFactoryBean postgresqlDb1EntityManagerFactory(
EntityManagerFactoryBuilder builder,
@Qualifier("postgresqlDb1DataSource") DataSource dataSource) {
return builder.dataSource(dataSource)
.packages(new String[] {"com.example.app.transaction.entity"}).persistenceUnit("postgres")
.build();
}
@Bean(name = "postgresqlDb1TransactionManager")
public PlatformTransactionManager postgresqlDb1TransactionManager(
@Qualifier("postgresqlDb1EntityManagerFactory") EntityManagerFactory entityManagerFactory) {
return new JpaTransactionManager(entityManagerFactory);
}
}
package com.example.app.config;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Qualifier;
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 jakarta.persistence.EntityManagerFactory;
@Configuration
@EnableJpaRepositories(basePackages = "com.example.app.employee.repository",
entityManagerFactoryRef = "sqlserverDb1EntityManagerFactory",
transactionManagerRef = "sqlserverDb1TransactionManager")
public class SqlserverDb1Config {
@Bean(name = "sqlserverDb1EntityManagerFactory")
public LocalContainerEntityManagerFactoryBean sqlserverDb1EntityManagerFactory(
EntityManagerFactoryBuilder builder,
@Qualifier("sqlserverDb1DataSource") DataSource dataSource) {
return builder.dataSource(dataSource).packages(new String[] {"com.example.app.employee.entity"})
.build();
}
@Bean(name = "sqlserverDb1TransactionManager")
public PlatformTransactionManager sqlserverDb1TransactionManager(
@Qualifier("sqlserverDb1EntityManagerFactory") EntityManagerFactory entityManagerFactory) {
return new JpaTransactionManager(entityManagerFactory);
}
}
package com.example.app.config;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Qualifier;
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 jakarta.persistence.EntityManagerFactory;
@Configuration
@EnableJpaRepositories(basePackages = "com.example.app.student.repository",
entityManagerFactoryRef = "oracleDb1EntityManagerFactory",
transactionManagerRef = "oracleDb1TransactionManager")
public class OracleDb1Config {
@Bean(name = "oracleDb1EntityManagerFactory")
public LocalContainerEntityManagerFactoryBean oracleDb1EntityManagerFactory(
EntityManagerFactoryBuilder builder,
@Qualifier("oracleDb1DataSource") DataSource dataSource) {
return builder.dataSource(dataSource).packages(new String[] {"com.example.app.student.entity"})
.build();
}
@Bean(name = "oracleDb1TransactionManager")
public PlatformTransactionManager oracleDb1TransactionManager(
@Qualifier("oracleDb1EntityManagerFactory") EntityManagerFactory entityManagerFactory) {
return new JpaTransactionManager(entityManagerFactory);
}
}
Step 5: Create Entity Classes
Create separate entity classes for each database, annotating them with @Entity and defining their relationships. Make sure the entities correspond to the tables in their respective databases. For example:
package com.example.app.product.entity;
import java.util.Date;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.Data;
@Data
@EntityListeners(AuditingEntityListener.class)
@Entity
@Table(name = "product")
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "name")
private String name;
@Column(name = "price")
private double price;
@Column(name = "deleted")
private boolean deleted;
@Column(name = "created_by")
private Date createdBy;
@Column(name = "modified_by")
private Date modifiedBy;
@CreatedDate
@Column(name = "created_date")
private Date createdDate;
@LastModifiedDate
@Column(name = "modified_date")
private Date modifiedDate;
}
package com.example.app.customer.entity;
import java.util.Date;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.Data;
@Data
@EntityListeners(AuditingEntityListener.class)
@Entity
@Table(name = "customer")
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "first_name")
private String firstName;
@Column(name = "last_name")
private String lastName;
@Column(name = "email")
private String email;
@Column(name = "deleted")
private boolean deleted;
@Column(name = "created_by")
private Date createdBy;
@Column(name = "modified_by")
private Date modifiedBy;
@CreatedDate
@Column(name = "created_date")
private Date createdDate;
@LastModifiedDate
@Column(name = "modified_date")
private Date modifiedDate;
}
package com.example.app.transaction.entity;
import java.util.Date;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.Data;
@Data
@EntityListeners(AuditingEntityListener.class)
@Entity
@Table(name = "transaction")
public class Transaction {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "sender_id")
private String senderId;
@Column(name = "receiver_id")
private String receiverId;
@Column(name = "amount")
private double amount;
@Column(name = "fee")
private double fee;
@Column(name = "status")
private int status;
@Column(name = "created_by")
private Date createdBy;
@Column(name = "modified_by")
private Date modifiedBy;
@CreatedDate
@Column(name = "created_date")
private Date createdDate;
@LastModifiedDate
@Column(name = "modified_date")
private Date modifiedDate;
}
package com.example.app.employee.entity;
import java.util.Date;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.Data;
@Data
@EntityListeners(AuditingEntityListener.class)
@Entity
@Table(name = "employee")
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "first_name")
private String firstName;
@Column(name = "last_name")
private String lastName;
@Column(name = "email")
private String email;
@Column(name = "deleted")
private boolean deleted;
@Column(name = "created_by")
private Date createdBy;
@Column(name = "modified_by")
private Date modifiedBy;
@CreatedDate
@Column(name = "created_date")
private Date createdDate;
@LastModifiedDate
@Column(name = "modified_date")
private Date modifiedDate;
}
package com.example.app.student.entity;
import java.util.Date;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.SequenceGenerator;
import jakarta.persistence.Table;
import lombok.Data;
@Data
@EntityListeners(AuditingEntityListener.class)
@Entity
@Table(name = "student")
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "student_seq_gen")
@SequenceGenerator(name = "student_seq_gen", sequenceName = "student_seq", allocationSize = 1)
private Long id;
@Column(name = "first_name")
private String firstName;
@Column(name = "last_name")
private String lastName;
@Column(name = "email")
private String email;
@Column(name = "deleted")
private boolean deleted;
@Column(name = "created_by")
private Date createdBy;
@Column(name = "modified_by")
private Date modifiedBy;
@CreatedDate
@Column(name = "created_date")
private Date createdDate;
@LastModifiedDate
@Column(name = "modified_date")
private Date modifiedDate;
}
Step 6: Create Repositories
Create separate repository interfaces for each data source:
package com.example.app.product.repository;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import com.example.app.product.entity.Product;
@Repository
public interface ProductRepository extends CrudRepository<Product, Long> {
}
package com.example.app.customer.repository;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import com.example.app.customer.entity.Customer;
@Repository
public interface CustomerRepository extends CrudRepository<Customer, Long> {
}
package com.example.app.transaction.repository;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import com.example.app.transaction.entity.Transaction;
@Repository
public interface TransactionRepository extends CrudRepository<Transaction, Long> {
}
package com.example.app.employee.repository;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import com.example.app.employee.entity.Employee;
@Repository
public interface EmployeeRepository extends CrudRepository<Employee, Long> {
}
package com.example.app.student.repository;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import com.example.app.student.entity.Student;
@Repository
public interface StudentRepository extends CrudRepository<Student, Long> {
}