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.

Ray's Galactic Tech
Ray's Galactic Tech
Ray's Galactic Tech
How to Set Up Multi-DataSource in Spring Boot with MyBatis

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.xml

1. 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.Driver

2.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.Driver

3.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.

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.

JavaSpring BootMyBatisMulti-DataSourcedynamic-datasource
Ray's Galactic Tech
Written by

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!

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.