Spring Boot supports configuring multiple data sources in a single application. This is useful when working with different databases or schemas. Below is a step-by-step guide to set up multiple data sources.
1. Add Dependencies
Include the necessary dependencies for Spring Data JPA and your database drivers in pom.xml (for Maven projects):
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
2. Define Configuration in application.properties or application.yml
Example in application.properties:
# Primary DataSource spring.datasource.primary.url=jdbc:mysql://localhost:3306/primarydb spring.datasource.primary.username=root spring.datasource.primary.password=password spring.datasource.primary.driver-class-name=com.mysql.cj.jdbc.Driver # Secondary DataSource spring.datasource.secondary.url=jdbc:mysql://localhost:3306/secondarydb spring.datasource.secondary.username=root spring.datasource.secondary.password=password spring.datasource.secondary.driver-class-name=com.mysql.cj.jdbc.Driver
Example in application.yml:
spring:
datasource:
primary:
url: jdbc:mysql://localhost:3306/primarydb
username: root
password: password
driver-class-name: com.mysql.cj.jdbc.Driver
secondary:
url: jdbc:mysql://localhost:3306/secondarydb
username: root
password: password
driver-class-name: com.mysql.cj.jdbc.Driver
3. Configure DataSource Beans
Define separate DataSource, EntityManagerFactory, and TransactionManager beans for each database.
Example Configuration for Primary DataSource:
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
basePackages = "com.example.primary.repository",
entityManagerFactoryRef = "primaryEntityManagerFactory",
transactionManagerRef = "primaryTransactionManager"
)
public class PrimaryDataSourceConfig {
@Primary
@Bean(name = "primaryDataSource")
@ConfigurationProperties(prefix = "spring.datasource.primary")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
@Primary
@Bean(name = "primaryEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean primaryEntityManagerFactory(EntityManagerFactoryBuilder builder) {
return builder
.dataSource(primaryDataSource())
.packages("com.example.primary.entity")
.persistenceUnit("primary")
.build();
}
@Primary
@Bean(name = "primaryTransactionManager")
public PlatformTransactionManager primaryTransactionManager(
@Qualifier("primaryEntityManagerFactory") EntityManagerFactory entityManagerFactory) {
return new JpaTransactionManager(entityManagerFactory);
}
}
Example Configuration for Secondary DataSource:
@Configuration
@EnableJpaRepositories(
basePackages = "com.example.secondary.repository",
entityManagerFactoryRef = "secondaryEntityManagerFactory",
transactionManagerRef = "secondaryTransactionManager"
)
public class SecondaryDataSourceConfig {
@Bean(name = "secondaryDataSource")
@ConfigurationProperties(prefix = "spring.datasource.secondary")
public DataSource secondaryDataSource() {
return DataSourceBuilder.create().build();
}
@Bean(name = "secondaryEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean secondaryEntityManagerFactory(EntityManagerFactoryBuilder builder) {
return builder
.dataSource(secondaryDataSource())
.packages("com.example.secondary.entity")
.persistenceUnit("secondary")
.build();
}
@Bean(name = "secondaryTransactionManager")
public PlatformTransactionManager secondaryTransactionManager(
@Qualifier("secondaryEntityManagerFactory") EntityManagerFactory entityManagerFactory) {
return new JpaTransactionManager(entityManagerFactory);
}
}
4. Create Entities for Each DataSource
Define separate entities for each database.
Example for Primary Database:
@Entity
@Table(name = "users")
public class PrimaryUser {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
// Getters and Setters
}
Example for Secondary Database:
The SecondaryOrder class is mapped to the secondary database via the secondaryEntityManagerFactory configuration. This ensures that all operations on SecondaryOrder go through the secondary database.
@Entity
@Table(name = "orders")
public class SecondaryOrder {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String description;
// Getters and Setters
}
5. Create Repositories
Define separate repository interfaces for each database.
Example for Primary Database:
@Repository
public interface PrimaryUserRepository extends JpaRepository<PrimaryUser, Long> {
}
Example for Secondary Database:
@Repository
public interface SecondaryOrderRepository extends JpaRepository<SecondaryOrder, Long> {
}
6. Using Multiple DataSources
Inject the repositories to use them in your service or controller.
Example Service:
@Service
public class MultiDataSourceService {
private final PrimaryUserRepository primaryUserRepository;
private final SecondaryOrderRepository secondaryOrderRepository;
public MultiDataSourceService(PrimaryUserRepository primaryUserRepository, SecondaryOrderRepository secondaryOrderRepository) {
this.primaryUserRepository = primaryUserRepository;
this.secondaryOrderRepository = secondaryOrderRepository;
}
public void performDatabaseOperations() {
// Operations on primary database
PrimaryUser user = new PrimaryUser();
user.setName("John Doe");
primaryUserRepository.save(user);
// Operations on secondary database
SecondaryOrder order = new SecondaryOrder();
order.setDescription("Sample Order");
secondaryOrderRepository.save(order);
}
}
7. Best Practices
- Use Descriptive Names: Name your beans and configurations descriptively (e.g.,
primaryDataSource). - Separate Entities: Use different packages for entities belonging to different data sources.
- Centralized Configuration: Keep data source configurations in
application.propertiesorapplication.ymlfor easier management. - Test Thoroughly: Ensure queries and transactions work as expected for both data sources.
This setup allows seamless integration of multiple databases in a Spring Boot application.