Mastering Multiple Data Sources in Spring Boot 3: A Complete Guide

This article walks through configuring multiple data sources in Spring Boot 3, covering @Primary usage, YAML datasource definitions, bean setup with DataSourceBuilder, JPA entity manager factories, transaction managers, and unit testing to ensure each datasource operates independently.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Mastering Multiple Data Sources in Spring Boot 3: A Complete Guide

Environment: Spring Boot 3.4.2

1. Introduction

When you need to configure multiple data sources, one of them must be marked with @Primary because subsequent auto‑configuration components look for a single candidate by type.

If you define your own data source, Spring Boot’s default auto‑configuration will be disabled, so you need to provide a complete example.

2. Practical Example

2.1 Data Source Configuration

app:
  datasource:
    first:
      url: "jdbc:mysql://localhost:3306/ds1"
      username: "root"
      password: "123123"
      configuration:
        minimumIdle: 20
        maximumPoolSize: 20
    second:
      url: "jdbc:mysql://localhost:3306/ds2"
      username: "root"
      password: "123123"
      configuration:
        minimumIdle: 10
        maximumPoolSize: 10

This defines two data sources, first and second , with connection‑pool settings. Additional data sources can be added following the same pattern.

2.2 Multi‑DataSource Bean Configuration

@Configuration(proxyBeanMethods = false)
public class MultiDataSourcesConfiguration {

  @Bean
  @Primary
  @ConfigurationProperties("app.datasource.first")
  DataSourceProperties firstDataSourceProperties() {
    return new DataSourceProperties();
  }

  @Bean
  @Primary
  @ConfigurationProperties("app.datasource.first.configuration")
  HikariDataSource firstDataSource(DataSourceProperties firstDataSourceProperties) {
    return firstDataSourceProperties.initializeDataSourceBuilder()
        .type(HikariDataSource.class).build();
  }

  @Bean
  @ConfigurationProperties("app.datasource.second")
  DataSourceProperties secondDataSourceProperties() {
    return new DataSourceProperties();
  }

  @Bean
  @ConfigurationProperties("app.datasource.second.configuration")
  HikariDataSource secondDataSource(@Qualifier("secondDataSourceProperties") DataSourceProperties secondDataSourceProperties) {
    return secondDataSourceProperties.initializeDataSourceBuilder()
        .type(HikariDataSource.class).build();
  }
}

The method DataSourceProperties#initializeDataSourceBuilder internally uses DataSourceBuilder to create the actual DataSource instance. The @Primary annotation is optional; the logic mirrors Spring Boot’s own auto‑configuration.

2.3 JPA Configuration for Multiple Data Sources

Below is the configuration for the first datasource’s JPA settings; the second datasource follows the same pattern with different bean names.

app:
  jpa:
    first:
      generateDdl: false
      openInView: true
      show-sql: true
    second:
      generateDdl: false
      openInView: true
      show-sql: true
@Configuration(proxyBeanMethods = false)
@EnableJpaRepositories(
    basePackages = {"com.pack.ds1"},
    entityManagerFactoryRef = "firstEntityManagerFactory",
    transactionManagerRef = "firstJPATransactionManager")
public class FirstEntityManagerFactoryConfiguration {

  @Bean
  @ConfigurationProperties("app.jpa.first")
  JpaProperties firstJpaProperties() {
    return new JpaProperties();
  }

  @Bean
  LocalContainerEntityManagerFactoryBean firstEntityManagerFactory(
      DataSource firstDataSource, JpaProperties firstJpaProperties) {
    Map<String, Object> properties = new HashMap<>();
    properties.put("hibernate.hbm2ddl.auto", "update");
    EntityManagerFactoryBuilder builder = createEntityManagerFactoryBuilder(firstJpaProperties);
    return builder
        .dataSource(firstDataSource)
        .packages("com.pack.ds1")
        .properties(properties)
        .persistenceUnit("firstDs")
        .build();
  }

  private EntityManagerFactoryBuilder createEntityManagerFactoryBuilder(JpaProperties jpaProperties) {
    JpaVendorAdapter jpaVendorAdapter = createJpaVendorAdapter(jpaProperties);
    return new EntityManagerFactoryBuilder(jpaVendorAdapter, jpaProperties.getProperties(), null);
  }

  private JpaVendorAdapter createJpaVendorAdapter(JpaProperties jpaProperties) {
    return new HibernateJpaVendorAdapter();
  }

  @Bean
  PlatformTransactionManager firstJPATransactionManager(EntityManagerFactory firstEntityManagerFactory) {
    return new JpaTransactionManager(firstEntityManagerFactory);
  }
}

Repeat the same configuration for the second datasource, changing package names and bean identifiers accordingly.

3. Testing

Entity definitions and repositories:

@Entity
@Table(name = "t_student")
public class Student {
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;
  private String name;
}

public interface StudentRepository extends JpaRepository<Student, Long> {}

@Entity
@Table(name = "t_teacher")
public class Teacher {
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;
  private String name;
}

public interface TeacherRepository extends JpaRepository<Teacher, Long> {}

Unit test saving entities to different databases:

@Resource
private StudentRepository studentRepository;
@Resource
private TeacherRepository teacherRepository;

@Test
public void test() {
  Student student = new Student();
  student.setName("张三");
  Teacher teacher = new Teacher();
  teacher.setName("王老师");
  studentRepository.save(student);
  teacherRepository.save(teacher);
}

Running the test stores Student in the first datasource and Teacher in the second datasource, demonstrating independent persistence.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaBackend DevelopmentConfigurationSpring BootjpaMultiple Data Sources
Spring Full-Stack Practical Cases
Written by

Spring Full-Stack Practical Cases

Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.