Spring Boot
DataSource
JdbcTemplate
Multi-tenancy
Spring Framework

Multiple DataSource and JdbcTemplate in Spring Boot 1.1.0

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Using multiple data sources with separate JdbcTemplate beans in older Spring Boot versions requires explicit bean wiring. Autoconfiguration defaults are designed for one primary datasource, so multi-source setups must define naming and qualifier rules carefully. A stable configuration isolates properties, templates, and transaction boundaries for each database.

Define Separate Property Namespaces

Use dedicated prefixes for each datasource.

properties
1app.datasource.primary.url=jdbc:mysql://localhost:3306/main_db
2app.datasource.primary.username=main
3app.datasource.primary.password=mainpass
4
5app.datasource.reporting.url=jdbc:mysql://localhost:3306/report_db
6app.datasource.reporting.username=report
7app.datasource.reporting.password=reportpass

Clear namespace separation reduces misconfiguration risk.

Create Named DataSource Beans

Define one bean per datasource with explicit names.

java
1import javax.sql.DataSource;
2import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
3import org.springframework.boot.context.properties.ConfigurationProperties;
4import org.springframework.context.annotation.Bean;
5import org.springframework.context.annotation.Configuration;
6
7@Configuration
8public class DataSourceConfig {
9
10    @Bean(name = "primaryDataSource")
11    @ConfigurationProperties(prefix = "app.datasource.primary")
12    public DataSource primaryDataSource() {
13        return DataSourceBuilder.create().build();
14    }
15
16    @Bean(name = "reportingDataSource")
17    @ConfigurationProperties(prefix = "app.datasource.reporting")
18    public DataSource reportingDataSource() {
19        return DataSourceBuilder.create().build();
20    }
21}

Bean names must stay stable because repositories and templates depend on them.

Create Dedicated JdbcTemplate Beans

Bind each template to its datasource.

java
1import javax.sql.DataSource;
2import org.springframework.beans.factory.annotation.Qualifier;
3import org.springframework.context.annotation.Bean;
4import org.springframework.context.annotation.Configuration;
5import org.springframework.jdbc.core.JdbcTemplate;
6
7@Configuration
8public class JdbcTemplateConfig {
9
10    @Bean(name = "primaryJdbcTemplate")
11    public JdbcTemplate primaryJdbcTemplate(
12            @Qualifier("primaryDataSource") DataSource ds) {
13        return new JdbcTemplate(ds);
14    }
15
16    @Bean(name = "reportingJdbcTemplate")
17    public JdbcTemplate reportingJdbcTemplate(
18            @Qualifier("reportingDataSource") DataSource ds) {
19        return new JdbcTemplate(ds);
20    }
21}

Without qualifiers, injection may fail or choose wrong bean.

Inject Templates Explicitly in Repositories

Repository constructors should declare intended template by qualifier.

java
1import org.springframework.beans.factory.annotation.Qualifier;
2import org.springframework.jdbc.core.JdbcTemplate;
3import org.springframework.stereotype.Repository;
4
5@Repository
6public class ReportRepository {
7    private final JdbcTemplate reportingJdbcTemplate;
8
9    public ReportRepository(@Qualifier("reportingJdbcTemplate") JdbcTemplate reportingJdbcTemplate) {
10        this.reportingJdbcTemplate = reportingJdbcTemplate;
11    }
12}

This keeps database ownership obvious in code.

Transaction Boundaries Per Datasource

If you use transactions, configure one transaction manager per datasource and reference the correct one in service methods.

For cross-database writes, prefer workflow-level compensation over distributed transactions unless strict atomicity is mandatory.

Explicit boundaries reduce surprise rollbacks and lock interactions.

Operational Hardening

Multi-source systems need per-datasource monitoring and connection pool tuning. Transactional and reporting workloads usually need different limits and timeout settings.

Track metrics separately for each datasource:

  • Connection pool usage.
  • Query latency.
  • Error rates.
  • Health status.

Separate observability makes incident triage much faster.

Test Isolation and Wiring

Create integration tests that prove each repository hits intended database. Use distinct test datasets per schema so wrong routing is obvious.

A simple staging test is intentionally disabling one datasource and confirming only dependent components fail.

This validates isolation and prevents cascading startup failures.

Legacy Version Considerations

Spring Boot 1.1.0 is legacy. Behavior and configuration conventions differ from modern Boot releases. If migration is planned, keep regression tests around datasource wiring before and after upgrade.

Migration should be incremental:

  1. Freeze behavior with tests.
  2. Upgrade dependencies.
  3. Revalidate datasource and template wiring.
  4. Recheck transaction semantics.

Documentation and Ownership

Maintain a short ownership document listing which repository package maps to each datasource and template bean. This prevents accidental cross-database usage as teams grow.

A lightweight architecture test that blocks forbidden package access can enforce this rule and reduce long-term wiring drift.

Common Pitfalls

  • Defining multiple datasources without unique bean names.
  • Forgetting qualifiers in JdbcTemplate injection.
  • Assuming one default transaction manager is enough.
  • Mixing repository ownership across databases.
  • Upgrading framework version without wiring regression tests.

Summary

  • Multi-datasource Boot setup requires explicit naming and qualifiers.
  • Create one datasource and one template bean per backend.
  • Keep repository ownership boundaries clear.
  • Monitor and tune each datasource independently.
  • Protect behavior with integration tests, especially on legacy versions.

Course illustration
Course illustration

All Rights Reserved.