Backend Development 16 min read

Integrating Spring Boot with MyBatis and Multiple Data Sources (Dynamic DataSource)

This article explains how to integrate Spring Boot with MyBatis, configure a single Druid data source, implement a dynamic routing data source for multiple databases, and use custom annotations and AOP to switch databases at runtime, including full code examples and transaction management.

IT Services Circle
IT Services Circle
IT Services Circle
Integrating Spring Boot with MyBatis and Multiple Data Sources (Dynamic DataSource)

In 2019 a colleague needed to connect a medical system to a Hospital Information System (HIS) and the solution chosen was view‑based integration. The article uses this scenario to demonstrate Spring Boot and MyBatis integration with single and multiple data sources.

What is a multi‑data source? A single‑application normally uses one DataSource . When two or more databases are required, multiple DataSource beans must be configured.

Example bean definition for a Druid data source:

@Bean(name = "dataSource")
public DataSource dataSource() {
    DruidDataSource druid = new DruidDataSource();
    druid.setUrl(url);
    druid.setUsername(username);
    druid.setPassword(password);
    druid.setDriverClassName(driverClassName);
    return druid;
}

The application.properties contains the Druid pool settings (initial size, max active, validation query, etc.).

Integrating MyBatis is straightforward: add the mybatis-spring-boot-starter dependency, configure the mybatis prefix in the global properties, and optionally use @MapperScan to scan mapper interfaces.

Typical MyBatis SqlSessionFactory bean:

@Bean("sqlSessionFactory1")
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
    SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
    factory.setDataSource(dataSource);
    factory.setMapperLocations(new PathMatchingResourcePatternResolver()
        .getResources("classpath*:/mapper/**/*.xml"));
    org.apache.ibatis.session.Configuration cfg = new org.apache.ibatis.session.Configuration();
    cfg.setMapUnderscoreToCamelCase(true);
    cfg.setDefaultFetchSize(100);
    cfg.setDefaultStatementTimeout(30);
    factory.setConfiguration(cfg);
    return factory.getObject();
}

Dynamic DataSource – Spring provides AbstractRoutingDataSource which holds a Map<Object,Object> targetDataSources . Subclasses override determineCurrentLookupKey() to return a key stored in a ThreadLocal holder.

public class DataSourceHolder {
    private static final ThreadLocal
dataSources = new InheritableThreadLocal<>();
    public static void setDataSource(String ds) { dataSources.set(ds); }
    public static String getDataSource() { return dataSources.get(); }
    public static void clearDataSource() { dataSources.remove(); }
}

The custom dynamic data source implementation:

public class DynamicDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceHolder.getDataSource();
    }
    public DynamicDataSource(DataSource defaultDs, Map<Object,Object> targetDs) {
        super.setDefaultTargetDataSource(defaultDs);
        super.setTargetDataSources(targetDs);
        super.afterPropertiesSet();
    }
}

A custom annotation @SwitchSource and an AOP aspect are used to switch the data source before a method executes and clear it afterwards:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SwitchSource {
    String DEFAULT_NAME = "hisDataSource";
    String value() default DEFAULT_NAME;
}

@Aspect
@Order(1)
@Component
@Slf4j
public class DataSourceAspect {
    @Pointcut("@annotation(SwitchSource)")
    public void pointcut() {}

    @Before("pointcut()")
    public void beforeOpt(JoinPoint jp) {
        Method method = ((MethodSignature) jp.getSignature()).getMethod();
        SwitchSource ss = method.getAnnotation(SwitchSource.class);
        log.info("[Switch DataSource]:" + ss.value());
        DataSourceHolder.setDataSource(ss.value());
    }

    @After("pointcut()")
    public void afterOpt() {
        DataSourceHolder.clearDataSource();
        log.info("[Switch Default DataSource]");
    }
}

Two concrete DataSource beans are defined – the default one and the HIS data source (prefix spring.datasource.his ). They are injected into the DynamicDataSource constructor.

@ConfigurationProperties(prefix = "spring.datasource")
@Bean("dataSource")
public DataSource dataSource() { return new DruidDataSource(); }

@Bean(name = SwitchSource.DEFAULT_NAME)
@ConfigurationProperties(prefix = "spring.datasource.his")
public DataSource hisDataSource() { return DataSourceBuilder.create().build(); }

The dynamic data source is then used to create a primary SqlSessionFactory and a primary transaction manager:

@Primary
@Bean("sqlSessionFactory2")
public SqlSessionFactory sqlSessionFactoryBean(DynamicDataSource dynamicDataSource) throws Exception {
    SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
    factory.setDataSource(dynamicDataSource);
    // configuration omitted for brevity
    return factory.getObject();
}

@Primary
@Bean("transactionManager2")
public PlatformTransactionManager annotationDrivenTransactionManager(DynamicDataSource ds) {
    return new DataSourceTransactionManager(ds);
}

Usage example – annotate a service method with @SwitchSource (and optionally @Transactional(propagation = Propagation.NOT_SUPPORTED) ) to run the method against the HIS database; after the method finishes the default data source is restored.

@Transactional(propagation = Propagation.NOT_SUPPORTED)
@SwitchSource
@Override
public List
list() {
    return hisDeptInfoMapper.listDept();
}

The article concludes that Spring Boot, MyBatis and a dynamic routing data source can be combined to support multiple databases in a clean, extensible way, even though such scenarios are not everyday requirements.

JavaSpring BootMyBatisDatabase IntegrationDynamic DataSourcemultiple data sources
IT Services Circle
Written by

IT Services Circle

Delivering cutting-edge internet insights and practical learning resources. We're a passionate and principled IT media platform.

0 followers
Reader feedback

How this landed with the community

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