This project demonstrates how to configure Spring Data JDBC with multiple independent datasources, each with its own repositories, entities, and database migrations.
The application connects to two separate MySQL databases:
- db1: Contains
Accountentities - db2: Contains
Reportentities
A single endpoint (GET /) demonstrates fetching and merging data from both datasources into a unified response.
Each datasource requires its own set of beans to maintain complete isolation:
db1CciDataSource- HikariCP datasourcedb1TxManager- Transaction managerdb1JdbcOps- NamedParameterJdbcOperationsdb1MappingContext- JdbcMappingContext for entity mappingdb1Converter- JDBC converterdb1Dialect- MySQL dialectdb1DataAccessStrategy- Data access strategydb1NamedParameterJdbcOps- JdbcAggregateOperations template
Same structure as DB1, with all beans marked @Primary since Spring Data JDBC auto-configuration expects primary beans.
Each datasource has its own repository configuration:
@EnableJdbcRepositories(
basePackages = "com.sparrowlogic.springdatajdbcmultipledatasources.db1.repository",
jdbcAggregateOperationsRef = "db1NamedParameterJdbcOps",
transactionManagerRef = "db1TxManager"
)@EnableJdbcRepositories(
basePackages = "com.sparrowlogic.springdatajdbcmultipledatasources.db2.repository",
jdbcAggregateOperationsRef = "db2NamedParameterJdbcOps",
transactionManagerRef = "db2TxManager"
)Flyway is configured separately for each datasource:
- Location:
classpath:db/migration/db1 - Creates
accounttable
- Location:
classpath:db/migration/db2 - Creates
reporttable
Both run automatically on application startup via initMethod = "migrate".
ServiceThatDependsOnTwoDataSources demonstrates cross-datasource operations:
- Injects both
AccountRepositoryandReportRepository - Saves entities to both databases
- Returns merged response containing data from both sources
IndexController exposes a single endpoint:
GET /- Creates and returns anAccountfrom db1 and aReportfrom db2
- Java 17+
- Docker (for MySQL containers)
- Maven
docker run -d --name mysql-db1 -p 33060:3306 \
-e MYSQL_ROOT_PASSWORD=password \
-e MYSQL_DATABASE=first \
-e MYSQL_USER=db1user \
-e MYSQL_PASSWORD=password \
mysql:latest
docker run -d --name mysql-db2 -p 33061:3306 \
-e MYSQL_ROOT_PASSWORD=password \
-e MYSQL_DATABASE=second \
-e MYSQL_USER=db2user \
-e MYSQL_PASSWORD=password \
mysql:latestmvn spring-boot:runVisit http://localhost:8080 or:
curl http://localhost:8080Response:
{
"account": {
"id": 1,
"name": "test"
},
"report": {
"id": 1,
"reportName": "test"
}
}The response demonstrates successful integration of entities from two independent datasources.
AccountRepositoryTest- Tests db1 repository with@Transactional("db1TxManager")ReportRepositoryTest- Tests db2 repository with@Transactional("db2TxManager")
Each test uses its own transaction manager for proper isolation.
ServiceThatDependsOnTwoDataSourcesTest- Unit test with mocked repositories
IndexControllerTest- Web layer test using MockMvc
TestcontainersConfiguration creates two independent MySQL containers for integration tests:
db1Container- For first datasourcedb2Container- For second datasource
Properties are dynamically injected via @DynamicPropertySource.
spring:
flyway:
enabled: false # Disabled in favor of manual configuration
data:
jdbc:
repositories:
enabled: false # Disabled in favor of manual @EnableJdbcRepositories
datasource:
db1:
url: jdbc:mysql://localhost:33060/first
username: db1user
password: password
db2:
url: jdbc:mysql://localhost:33061/second
username: db2user
password: password-
Complete Isolation: Each datasource needs its own DataSource, TransactionManager, JdbcOperations, MappingContext, Converter, Dialect, and DataAccessStrategy.
-
Repository Separation: Use
@EnableJdbcRepositorieswith distinctbasePackages,jdbcAggregateOperationsRef, andtransactionManagerReffor each datasource. -
Primary Beans: Mark one set of beans as
@Primaryto satisfy Spring Data JDBC's auto-configuration expectations. -
Transaction Management: Specify transaction manager explicitly in tests using
@Transactional("txManagerName"). -
Flyway Isolation: Configure separate Flyway instances with distinct migration locations for each datasource.