Dynamic Multi-DataSource Routing with MyBatis-Plus and Druid in Spring Boot

Maven Dependencies

Include the required starters in your pom.xml:

<dependencies>
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.5.5</version>
    </dependency>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid-spring-boot-3-starter</artifactId>
        <version>1.2.20</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.33</version>
    </dependency>
</dependencies>

Connection Pool Settings

Configure distinct connection pools in application.yml:

spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    primary:
      jdbc-url: jdbc:mysql://127.0.0.1:3306/main_db?useSSL=false&serverTimezone=UTC
      username: app_user
      password: app_pass
      driver-class-name: com.mysql.cj.jdbc.Driver
    auxiliary:
      jdbc-url: jdbc:mysql://127.0.0.1:3306/analytics_db?useSSL=false&serverTimezone=UTC
      username: readonly_user
      password: readonly_pass
      driver-class-name: com.mysql.cj.jdbc.Driver
    druid:
      initial-size: 5
      max-active: 20
      min-idle: 5
      max-wait: 60000

DataSource Routing Infrastructure

Implement the routing mechanism using Spring's AbstractRoutingDataSource:

public enum DataSourceKey {
    PRIMARY, AUXILIARY
}

public class RoutingContext {
    private static final ThreadLocal<DataSourceKey> contextHolder = new ThreadLocal<>();
    
    public static void set(DataSourceKey key) {
        contextHolder.set(key);
    }
    
    public static DataSourceKey get() {
        return contextHolder.get();
    }
    
    public static void clear() {
        contextHolder.remove();
    }
}

public class DynamicRoutingDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        return RoutingContext.get();
    }
}

Bean Configuration

Register the data sources and MyBatis-Plus components:

@Configuration
@MapperScan(basePackages = "com.example.persistence.mapper", 
             sqlSessionFactoryRef = "sqlSessionFactory")
public class DataSourceArchitecture {

    @Bean(name = "primaryPool")
    @ConfigurationProperties(prefix = "spring.datasource.primary")
    public DataSource primaryPool() {
        return DataSourceBuilder.create().type(DruidDataSource.class).build();
    }

    @Bean(name = "auxiliaryPool")
    @ConfigurationProperties(prefix = "spring.datasource.auxiliary")
    public DataSource auxiliaryPool() {
        return DataSourceBuilder.create().type(DruidDataSource.class).build();
    }

    @Bean(name = "routingDataSource")
    @Primary
    public DataSource routingDataSource(
            @Qualifier("primaryPool") DataSource primary,
            @Qualifier("auxiliaryPool") DataSource auxiliary) {
        
        DynamicRoutingDataSource router = new DynamicRoutingDataSource();
        Map<Object, Object> pools = new HashMap<>();
        pools.put(DataSourceKey.PRIMARY, primary);
        pools.put(DataSourceKey.AUXILIARY, auxiliary);
        
        router.setTargetDataSources(pools);
        router.setDefaultTargetDataSource(primary);
        return router;
    }

    @Bean(name = "sqlSessionFactory")
    public SqlSessionFactory sqlSessionFactory(
            @Qualifier("routingDataSource") DataSource routingDS) throws Exception {
        MybatisSqlSessionFactoryBean factoryBean = new MybatisSqlSessionFactoryBean();
        factoryBean.setDataSource(routingDS);
        factoryBean.setMapperLocations(
            new PathMatchingResourcePatternResolver()
                .getResources("classpath*:mapper/**/*.xml"));
        return factoryBean.getObject();
    }
}

Switching Data Sources at Runtime

Utilize the routing context within your service layer:

@Service
public class OrderProcessingService {
    
    @Autowired
    private OrderMapper orderMapper;
    
    public List<Order> fetchFromPrimary() {
        RoutingContext.set(DataSourceKey.PRIMARY);
        try {
            return orderMapper.selectActiveOrders();
        } finally {
            RoutingContext.clear();
        }
    }
    
    public List<Report> fetchAnalytics() {
        RoutingContext.set(DataSourceKey.AUXILIARY);
        try {
            return orderMapper.selectHistoricalData();
        } finally {
            RoutingContext.clear();
        }
    }
}

Alternatively, implement AOP-based switching using custom annotations:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TargetDataSource {
    DataSourceKey value() default DataSourceKey.PRIMARY;
}

@Aspect
@Component
public class DataSourceInterceptor {
    
    @Around("@annotation(targetDataSource)")
    public Object route(ProceedingJoinPoint point, TargetDataSource targetDataSource) 
            throws Throwable {
        RoutingContext.set(targetDataSource.value());
        try {
            return point.proceed();
        } finally {
            RoutingContext.clear();
        }
    }
}

Usage Example

Apply the annotation to repository methods:

public interface InventoryRepository {
    
    @TargetDataSource(DataSourceKey.PRIMARY)
    List<Stock> getCurrentStock();
    
    @TargetDataSource(DataSourceKey.AUXILIARY)
    List<StockTrend> getStockTrends();
}

Tags: mybatis-plus druid Spring Boot multi-datasource Dynamic Routing

Posted on Wed, 02 Sep 2026 16:17:10 +0000 by doublebassdanny