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.
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: 10This 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.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
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.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
