Configuring MyBatis with Multiple Data Sources in Spring Boot
Confgiuring multiple databases in a single Spring Boot application with MyBatis requires defining separate DataSource, SqlSessionFactory, and SqlSessionTemplate beans per database, and mapping each mapper package to the appropriate factory/template.
Maven dependencies
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.16</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
application.properties
# DataSource A
spring.datasource.alpha.url=jdbc:mysql://localhost:3306/db_alpha?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
spring.datasource.alpha.username=root
spring.datasource.alpha.password=123456
spring.datasource.alpha.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.alpha.type=com.alibaba.druid.pool.DruidDataSource
# DataSource B
spring.datasource.beta.url=jdbc:mysql://localhost:3306/db_beta?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
spring.datasource.beta.username=root
spring.datasource.beta.password=123456
spring.datasource.beta.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.beta.type=com.alibaba.druid.pool.DruidDataSource
DataSource beans
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import javax.sql.DataSource;
@Configuration
public class DataSourceConfig {
@Bean(name = "alphaDataSource")
@Primary
@ConfigurationProperties(prefix = "spring.datasource.alpha")
public DataSource alphaDataSource() {
return DruidDataSourceBuilder.create().build();
}
@Bean(name = "betaDataSource")
@ConfigurationProperties(prefix = "spring.datasource.beta")
public DataSource betaDataSource() {
return DruidDataSourceBuilder.create().build();
}
}
MyBatis wiring for DataSource A
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import javax.sql.DataSource;
@Configuration
@MapperScan(
basePackages = "com.example.multids.alpha.mapper",
sqlSessionFactoryRef = "alphaSqlSessionFactory",
sqlSessionTemplateRef = "alphaSqlSessionTemplate"
)
public class MybatisAlphaConfig {
@Bean(name = "alphaSqlSessionFactory")
@Primary
public SqlSessionFactory alphaSqlSessionFactory(@Qualifier("alphaDataSource") DataSource ds) throws Exception {
SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
factory.setDataSource(ds);
factory.setMapperLocations(
new PathMatchingResourcePatternResolver().getResources("classpath*:mappers/alpha/*.xml")
);
return factory.getObject();
}
@Bean(name = "alphaSqlSessionTemplate")
@Primary
public SqlSessionTemplate alphaSqlSessionTemplate(
@Qualifier("alphaSqlSessionFactory") SqlSessionFactory sf
) {
return new SqlSessionTemplate(sf);
}
}
MyBatis wiring for DataSource B
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import javax.sql.DataSource;
@Configuration
@MapperScan(
basePackages = "com.example.multids.beta.mapper",
sqlSessionFactoryRef = "betaSqlSessionFactory",
sqlSessionTemplateRef = "betaSqlSessionTemplate"
)
public class MybatisBetaConfig {
@Bean(name = "betaSqlSessionFactory")
public SqlSessionFactory betaSqlSessionFactory(@Qualifier("betaDataSource") DataSource ds) throws Exception {
SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
factory.setDataSource(ds);
factory.setMapperLocations(
new PathMatchingResourcePatternResolver().getResources("classpath*:mappers/beta/*.xml")
);
return factory.getObject();
}
@Bean(name = "betaSqlSessionTemplate")
public SqlSessionTemplate betaSqlSessionTemplate(
@Qualifier("betaSqlSessionFactory") SqlSessionFactory sf
) {
return new SqlSessionTemplate(sf);
}
}
Mapper packages and XML placement
- Java interfaces
- com.example.multids.alpha.mapper for DataSource A
- com.example.multids.beta.mapper for DataSource B
- Mapper XML
- src/main/resources/mappers/alpha/*.xml
- src/main/resources/mappers/beta/*.xml
If mapper XML is stored under src/main/java instead of resources, include them during packaging by adding resources configuration to the build section of pom.xml:
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
</resource>
</resources>
</build>
Verification test
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
@SpringBootTest
class MultiDsTests {
@Autowired
private com.example.multids.alpha.mapper.BookMapper alphaBookMapper;
@Autowired
private com.example.multids.beta.mapper.BookMapper betaBookMapper;
@Test
void readFromBothDatabases() {
List<?> a = alphaBookMapper.selectAll();
List<?> b = betaBookMapper.selectAll();
System.out.println("alpha => " + a);
System.out.println("beta => " + b);
}
}