Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Configuring MyBatis with Multiple Data Sources in Spring Boot

Tech 2

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);
    }
}

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.