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.
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.DriverExplanation 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.
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.
Programmer DD
A tinkering programmer and author of "Spring Cloud Microservices in Action"
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.
