How to Set Up Multi-DataSource in Spring Boot with MyBatis
This guide walks through two approaches for configuring multiple databases in a Spring Boot and MyBatis project—native multi‑data‑source beans and the dynamic‑datasource‑spring‑boot‑starter—covering project structure, Maven dependencies, Java configuration, mapper setup, usage examples, common pitfalls, and a concise summary of when to choose each method.
Introduction
In many real‑world applications you need to access several databases such as a business DB, a log DB, or a reporting DB. This article presents two ways to achieve multi‑data‑source support in a Spring Boot + MyBatis project: a native configuration using separate DataSource beans and a quick solution based on dynamic-datasource-spring-boot-starter, together with common pitfalls.
Project Structure
The sample project follows this layout:
springboot-mybatis-multi-datasource/
├── src/main/java/com/example
│ ├── entity
│ │ └── User.java
│ ├── mapper
│ │ ├── primary
│ │ │ └── UserMapper.java
│ │ └── secondary
│ │ └── UserMapper.java
│ ├── config
│ │ ├── PrimaryDataSourceConfig.java
│ │ └── SecondaryDataSourceConfig.java
│ ├── service
│ │ └── UserService.java
│ └── controller
│ └── UserController.java
├── src/main/resources
│ ├── mapper
│ │ ├── primary
│ │ │ └── UserMapper.xml
│ │ └── secondary
│ │ └── UserMapper.xml
│ └── application.yml
└── pom.xml1. Maven Dependencies
<dependencies>
<!-- Spring Boot Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- MyBatis Starter -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.0</version>
</dependency>
<!-- MySQL Driver -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>2. Native Multi‑DataSource Configuration
2.1 application.yml
spring:
datasource:
primary:
jdbc-url: jdbc:mysql://localhost:3306/db1?useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
secondary:
jdbc-url: jdbc:mysql://localhost:3306/db2?useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver2.2 PrimaryDataSourceConfig.java
@Configuration
@MapperScan(basePackages = "com.example.mapper.primary", sqlSessionFactoryRef = "primarySqlSessionFactory")
public class PrimaryDataSourceConfig {
@Bean
@ConfigurationProperties("spring.datasource.primary")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
@Bean
public SqlSessionFactory primarySqlSessionFactory(@Qualifier("primaryDataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(dataSource);
// bind mapper XML files
sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver()
.getResources("classpath*:mapper/primary/*.xml"));
return sessionFactory.getObject();
}
@Bean
public DataSourceTransactionManager primaryTransactionManager(@Qualifier("primaryDataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}2.3 SecondaryDataSourceConfig.java
@Configuration
@MapperScan(basePackages = "com.example.mapper.secondary", sqlSessionFactoryRef = "secondarySqlSessionFactory")
public class SecondaryDataSourceConfig {
@Bean
@ConfigurationProperties("spring.datasource.secondary")
public DataSource secondaryDataSource() {
return DataSourceBuilder.create().build();
}
@Bean
public SqlSessionFactory secondarySqlSessionFactory(@Qualifier("secondaryDataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(dataSource);
sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver()
.getResources("classpath*:mapper/secondary/*.xml"));
return sessionFactory.getObject();
}
@Bean
public DataSourceTransactionManager secondaryTransactionManager(@Qualifier("secondaryDataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}2.4 Mapper Interfaces and XML
// Primary mapper
package com.example.mapper.primary;
import com.example.entity.User;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface UserMapper {
User selectById(int id);
}
// Secondary mapper (same method signature, different package)
package com.example.mapper.secondary;
import com.example.entity.User;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface UserMapper {
User selectById(int id);
}2.5 Service Usage Example
@Service
public class UserService {
@Autowired
private com.example.mapper.primary.UserMapper primaryUserMapper;
@Autowired
private com.example.mapper.secondary.UserMapper secondaryUserMapper;
public void doSomething() {
// use primary datasource
User user1 = primaryUserMapper.selectById(1);
// use secondary datasource
User user2 = secondaryUserMapper.selectById(1);
}
}2.6 Pitfalls
Mapper package separation : keep mappers for different data sources in distinct packages to avoid bean conflicts.
Mapper XML binding : each SqlSessionFactory must be configured with the correct mapper locations, otherwise MyBatis cannot find the XML files.
Transaction management : @Transactional binds to a single datasource by default; cross‑database transactions require a distributed‑transaction solution such as Seata or Atomikos.
Avoid overusing @Qualifier : package‑level @MapperScan already isolates the mappers, so additional @Qualifier annotations are usually unnecessary.
3. Quick Solution with dynamic‑datasource‑spring‑boot‑starter
3.1 Add Dependency
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>3.5.0</version>
</dependency>3.2 Configuration
spring:
datasource:
dynamic:
primary: master
datasource:
master:
url: jdbc:mysql://localhost:3306/db1?useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
slave:
url: jdbc:mysql://localhost:3306/db2?useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver3.3 Usage
@Service
public class UserService {
@DS("master")
public User getFromMaster(int id) {
return userMapper.selectById(id);
}
@DS("slave")
public User getFromSlave(int id) {
return userMapper.selectById(id);
}
}Note: the data source cannot be switched automatically inside a single transaction; @DS must be placed on the transaction entry method.
4. Summary
Native configuration : suitable when the number of data sources is fixed and you need fine‑grained transaction control.
dynamic‑datasource starter : ideal for scenarios with many data sources and frequent switching.
Regardless of the approach, proper transaction handling and mapper package management are essential to avoid conflicts or transaction failures.
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.
Ray's Galactic Tech
Practice together, never alone. We cover programming languages, development tools, learning methods, and pitfall notes. We simplify complex topics, guiding you from beginner to advanced. Weekly practical content—let's grow together!
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.
