How to Configure Multiple Data Sources with MyBatis in Spring Boot

Learn step-by-step how to set up and test multiple data sources in a Spring Boot application using MyBatis, covering property configuration, DataSource beans, mapper scanning, entity definitions, and verification through unit tests with complete code examples and key configuration notes.

Programmer DD
Programmer DD
Programmer DD
How to Configure Multiple Data Sources with MyBatis in Spring Boot

Adding Multiple Data Source Configuration

First set two database connections in application.properties:

spring.datasource.primary.jdbc-url=jdbc:mysql://localhost:3306/test1
spring.datasource.primary.username=root
spring.datasource.primary.password=123456
spring.datasource.primary.driver-class-name=com.mysql.cj.jdbc.Driver

spring.datasource.secondary.jdbc-url=jdbc:mysql://localhost:3306/test2
spring.datasource.secondary.username=root
spring.datasource.secondary.password=123456
spring.datasource.secondary.driver-class-name=com.mysql.cj.jdbc.Driver

Explanation and notes:

When configuring multiple data sources, add a name after spring.datasource (e.g., primary, secondary) to distinguish them; this prefix is used later during initialization.

Configuration keys differ between Spring Boot 2.x and 1.x: 2.x uses spring.datasource.secondary.jdbc-url, while 1.x uses spring.datasource.secondary.url. Mis‑matching these keys can cause errors such as

java.lang.IllegalArgumentException: jdbcUrl is required with driverClassName

.

The data source configuration format is the same regardless of whether you use JdbcTemplate, Spring Data JPA, or MyBatis.

Initializing Data Sources and MyBatis Configuration

Create a configuration class to load the properties, instantiate the data sources, and provide MyBatis factories for each.

@Configuration
public class DataSourceConfiguration {

    @Primary
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.primary")
    public DataSource primaryDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.secondary")
    public DataSource secondaryDataSource() {
        return DataSourceBuilder.create().build();
    }
}

Define separate MyBatis configuration classes for the primary and secondary data sources, using @MapperScan to bind mapper packages, sqlSessionFactory, and sqlSessionTemplate to each data source.

@Configuration
@MapperScan(
        basePackages = "com.didispace.chapter39.p",
        sqlSessionFactoryRef = "sqlSessionFactoryPrimary",
        sqlSessionTemplateRef = "sqlSessionTemplatePrimary")
public class PrimaryConfig {
    private final DataSource primaryDataSource;
    public PrimaryConfig(@Qualifier("primaryDataSource") DataSource primaryDataSource) {
        this.primaryDataSource = primaryDataSource;
    }
    @Bean
    public SqlSessionFactory sqlSessionFactoryPrimary() throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(primaryDataSource);
        return bean.getObject();
    }
    @Bean
    public SqlSessionTemplate sqlSessionTemplatePrimary() throws Exception {
        return new SqlSessionTemplate(sqlSessionFactoryPrimary());
    }
}
@Configuration
@MapperScan(
        basePackages = "com.didispace.chapter39.s",
        sqlSessionFactoryRef = "sqlSessionFactorySecondary",
        sqlSessionTemplateRef = "sqlSessionTemplateSecondary")
public class SecondaryConfig {
    private final DataSource secondaryDataSource;
    public SecondaryConfig(@Qualifier("secondaryDataSource") DataSource secondaryDataSource) {
        this.secondaryDataSource = secondaryDataSource;
    }
    @Bean
    public SqlSessionFactory sqlSessionFactorySecondary() throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(secondaryDataSource);
        return bean.getObject();
    }
    @Bean
    public SqlSessionTemplate sqlSessionTemplateSecondary() throws Exception {
        return new SqlSessionTemplate(sqlSessionFactorySecondary());
    }
}

Key points:

Use @MapperScan to specify the package containing entities and mappers for each data source, and to bind the corresponding sqlSessionFactory and sqlSessionTemplate.

Inject the correct DataSource into each configuration class via @Qualifier, matching the bean names defined in DataSourceConfiguration.

Define SqlSessionFactory and SqlSessionTemplate beans that reference the appropriate data source.

Entity and Mapper Definitions

For the primary data source:

@Data
@NoArgsConstructor
public class UserPrimary {
    private Long id;
    private String name;
    private Integer age;
    public UserPrimary(String name, Integer age) {
        this.name = name;
        this.age = age;
    }
}

public interface UserMapperPrimary {
    @Select("SELECT * FROM USER WHERE NAME = #{name}")
    UserPrimary findByName(@Param("name") String name);

    @Insert("INSERT INTO USER(NAME, AGE) VALUES(#{name}, #{age})")
    int insert(@Param("name") String name, @Param("age") Integer age);

    @Delete("DELETE FROM USER")
    int deleteAll();
}

For the secondary data source:

@Data
@NoArgsConstructor
public class UserSecondary {
    private Long id;
    private String name;
    private Integer age;
    public UserSecondary(String name, Integer age) {
        this.name = name;
        this.age = age;
    }
}

public interface UserMapperSecondary {
    @Select("SELECT * FROM USER WHERE NAME = #{name}")
    UserSecondary findByName(@Param("name") String name);

    @Insert("INSERT INTO USER(NAME, AGE) VALUES(#{name}, #{age})")
    int insert(@Param("name") String name, @Param("age") Integer age);

    @Delete("DELETE FROM USER")
    int deleteAll();
}

Testing the Configuration

Write a test class to verify that each mapper operates against its designated data source.

@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest
@Transactional
public class Chapter39ApplicationTests {

    @Autowired
    private UserMapperPrimary userMapperPrimary;

    @Autowired
    private UserMapperSecondary userMapperSecondary;

    @Before
    public void setUp() {
        // Clean tables before each test
        userMapperPrimary.deleteAll();
        userMapperSecondary.deleteAll();
    }

    @Test
    public void testMultipleDataSources() throws Exception {
        // Insert into primary
        userMapperPrimary.insert("AAA", 20);
        UserPrimary userPrimary = userMapperPrimary.findByName("AAA");
        Assert.assertEquals(20, userPrimary.getAge().intValue());
        // Verify not present in secondary
        UserSecondary userSecondary = userMapperSecondary.findByName("AAA");
        Assert.assertNull(userSecondary);

        // Insert into secondary
        userMapperSecondary.insert("BBB", 20);
        // Verify not present in primary
        userPrimary = userMapperPrimary.findByName("BBB");
        Assert.assertNull(userPrimary);
        // Verify present in secondary
        userSecondary = userMapperSecondary.findByName("BBB");
        Assert.assertEquals(20, userSecondary.getAge().intValue());
    }
}

The complete example code is available in the chapter3-9 directory of the GitHub or Gitee repositories.

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.

JavatestingSpring BootMyBatisDatabase ConfigurationMultiple Data Sources
Programmer DD
Written by

Programmer DD

A tinkering programmer and author of "Spring Cloud Microservices in Action"

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.