# baomidou/dynamic-datasource: a Spring Boot starter for routing between multiple datasources

> The starter does one job: it swaps the DataSource behind your Spring beans at runtime, using a @DS annotation, group names and load balancing. It is for teams that already run more than one database and want routing without rewriting their persistence layer.

**baomidou/dynamic-datasource** — dynamic datasource for springboot 多数据源 动态数据源 主从分离 读写分离 分布式事务 

- Repository: https://github.com/baomidou/dynamic-datasource
- Website: https://doc.xiuceyun.cn
- Stars: 5,186 · Forks: 1,238
- Language: Java
- License: Apache-2.0
- Published: 2026-09-23 · Updated: 2026-09-23 · Language: en
- Canonical page: https://hysenlabs.com/projects/baomidou-dynamic-datasource

## The problem: one Spring context, several physical databases

A Spring Boot application normally binds a single DataSource. The moment a second database appears, whether for read replicas, a separate reporting schema or a legacy Oracle instance, the wiring stops being declarative. Spring itself offers AbstractRoutingDataSource, but that gives you a lookup key mechanism and nothing else: no group names, no load balancing, no annotation, no encryption of credentials, no nested switching. Teams end up writing a custom routing class and a thread-local context holder, then discovering that the context is not cleared correctly after a nested service call.

baomidou/dynamic-datasource targets that gap. The README describes it as a launcher for quickly integrating multiple datasources into a Spring Boot application, and lists the scenarios it was designed around: pure multi-database, read-write separation, one primary with several replicas, and mixed configurations. The audience is a Java team already on Spring Boot that has a MyBatis or JdbcTemplate layer in place and does not want to change how that layer issues queries.

## How the @DS annotation and group naming actually route a query

The mechanism is an annotation plus a naming convention. Every datasource key in the configuration that contains an underscore is split at the first underscore, and the leading segment becomes a group name. So slave_1 and slave_2 both belong to the group slave. When you write @DS("slave"), the framework picks one member of that group using its load balancing algorithm. When you write @DS("slave_1"), you address one specific database and skip the balancing step.

Resolution order is stated in the README as a proximity rule: an explicit switch in a code block wins over a method annotation, which wins over a class annotation. The same section notes that @DS can be inherited from an abstract class or an interface, which matters if your service layer is built on a shared base class. Nested switching is supported, so a call chain of ServiceA, ServiceB, ServiceC can each declare a different target; the README gives that pattern explicitly, and it is the part most likely to surprise people who assume the outer annotation sticks for the whole request.

Two configuration keys control failure behaviour. spring.datasource.dynamic.primary sets the default datasource or group, and it defaults to master. spring.datasource.dynamic.strict defaults to false, which means an unmatched name silently falls back to the default datasource; setting it to true raises an exception instead. For a read-write split, the fallback behaviour is the difference between a typo sending reads to the primary and a typo failing the request.

## Picking the right starter module for your Spring Boot line

There are three starter artifacts and the choice is not optional. Spring Boot 1.5.x through 2.x.x uses dynamic-datasource-spring-boot-starter and supports JDK 8 and above. Spring Boot 3.x.x uses dynamic-datasource-spring-boot3-starter and requires JDK 17 or above. Spring Boot 4.x.x uses dynamic-datasource-spring-boot4-starter, also requiring JDK 17 or above, and the README states that support begins at version 4.5.0. Mixing the wrong module with your Boot version is the most common installation error, because the failure surfaces at startup rather than at compile time.

A minimal setup is a dependency plus a YAML block. The configuration below is the shape the README gives, with one primary and a two-member slave group. Note that driver-class-name became optional from 3.2.0 onward because of SPI loading, so you can drop it on recent versions. The password fields accept the ENC() wrapper for the built-in encryption, which the README points to the detailed documentation for rather than describing inline.

## First run: configuring master and slave, then annotating a service

Add the starter that matches your Spring Boot version, then declare the datasources. The block below defines a default master and a group named slave with two members. Because both keys start with slave_, they land in the same group and become eligible for load balancing when you target the group name.

```yaml
spring:
  datasource:
    dynamic:
      enabled: true
      primary: master
      strict: false
      datasource:
        master:
          url: jdbc:mysql://xx.xx.xx.xx:3306/dynamic
          username: root
          password: 123456
        slave_1:
          url: jdbc:mysql://xx.xx.xx.xx:3307/dynamic
          username: root
          password: 123456
        slave_2:
          url: jdbc:mysql://xx.xx.xx.xx:3308/dynamic
          username: root
          password: 123456
```

With that in place, the class-level annotation routes every method to the slave group, and the method-level annotation overrides it for one call. The README uses exactly this pattern with JdbcTemplate. Method annotations win over class annotations, so selectByCondition reads from slave_1 while everything else in the class spreads across the group.

## The annotation example the README gives

The following is the usage example from the README, unchanged in structure: a service class annotated at the type level, with one method narrowing the target to a specific database. The point to notice is that nothing about the query itself changes. The framework swaps the connection underneath the JdbcTemplate, and the SQL is untouched.

```java
@Service
@DS("slave")
public class UserServiceImpl implements UserService {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    public List selectAll() {
        return jdbcTemplate.queryForList("select * from user");
    }

    @Override
    @DS("slave_1")
    public List selectByCondition() {
        return jdbcTemplate.queryForList("select * from user where age >10");
    }
}
```

Run the application and call both methods. selectAll should be served by one of the two slave members, selectByCondition always by slave_1. If you cannot observe which database answered, the README does not describe a built-in query log for this, so verifying routing means checking the target database's own logs or query counters.

## Where the routing model stops being the right tool

The README is explicit that the framework only switches datasources and does not restrict what you do after the switch. That sentence is the boundary of the project. It is not a sharding middleware: it will not split one logical table across databases, it will not merge results from several databases into one result set, and it will not rewrite SQL. If your requirement is horizontal partitioning, this is the wrong layer.

There are quieter limitations. strict: false means a misspelled datasource name in @DS resolves to the primary without any warning, which is a silent correctness problem rather than a loud one; the README documents strict: true as the way to turn that into an exception, and it is worth deciding deliberately rather than accepting the default. The grace-destroy option defaults to false, and when enabled the README states that shutdown waits at most 10 seconds for active connections before forcing the close, so the window is bounded but not unlimited. Distributed transactions are offered through a Seata-based scheme and a local multi-datasource scheme, but both are options the README lists as features rather than defaults you get for free. Finally, the README notes that the older Kancloud documentation is no longer maintained and points to the current site, so older blog posts may describe configuration keys that have since changed.

## How it differs from Spring's own AbstractRoutingDataSource

The honest comparison is with the Spring Framework class the project effectively wraps. AbstractRoutingDataSource asks you to implement determineCurrentTargetDataSource(), which returns a lookup key, and to populate the targetDataSources map yourself. You own the thread-local context, you own the clearing, and you own the group concept if you want one. It has no annotation, no load balancing, no credential encryption and no nested-switching guarantee.

dynamic-datasource supplies all of those as conventions: the group naming rule based on the prefix before the first separator, the @DS annotation with its proximity ordering, the SpEL, session and header resolvers the README lists for dynamic parameters, and the ENC() wrapper for sensitive configuration values. The trade-off is that you adopt a naming convention and an annotation from a third-party library rather than a plain Spring interface, and your routing rules live in that library's resolution order. If you need only one alternate datasource and no grouping, the Spring class is fewer moving parts. If you need read-write splitting with several replicas and per-method overrides, the starter removes a meaningful amount of hand-written plumbing.

## Maintenance, licensing and the cost of upgrading

The repository is not archived, and the last push was on 2026-09-22. The most recent release listed is v4.50 (v4.5.0) from 2026-01-20, following v4.3.1 in 2024 and v4.3.0 in 2024, so the release cadence between the 4.3 and 4.5 lines spanned roughly a year and a half. That matters for planning: the project is not on a fast release train, and the 4.5.0 line is where Spring Boot 4 support begins, so a Boot 4 upgrade is also a starter upgrade.

The licence is Apache-2.0, which permits commercial use and modification, and the repository carries both LICENSE and license.txt at the top level. Apache-2.0 includes a patent grant and requires preservation of notices; a compliance review is a question for your own counsel, not something this description settles.

The upgrade cost is dominated by the module split. Because the artifact name changes with the Spring Boot generation, a Boot 2 to Boot 3 migration means swapping dynamic-datasource-spring-boot-starter for dynamic-datasource-spring-boot3-starter and moving to JDK 17. That is a build-file change plus a JDK baseline change, and it happens alongside whatever else your Boot upgrade requires. The repository layout shows the modules are built together with Gradle, so the three starters share a common module and version line.

## Conclusion

Adopt it when you already have several physical databases and want routing decided by annotation rather than by hand-built AbstractRoutingDataSource subclasses, and when you can pin the starter module to your Spring Boot line. Do not adopt it as a sharding layer or as a replacement for a connection pool: the README states the framework only switches datasources and leaves CRUD to you. Before rollout, verify the exact starter artifact against your Spring Boot and JDK combination, and confirm how strict mode should behave for your group names.

## FAQ

### How can I dynamically connect to multiple data sources in Spring Boot with baomidou/dynamic-datasource?

Declare each datasource under spring.datasource.dynamic.datasource with a key, then annotate a class or method with @DS naming either a specific datasource or a group. Keys that share a prefix before the first separator form a group, and targeting the group name makes the framework choose a member by load balancing.

### What is a dynamic data source in baomidou/dynamic-datasource?

In this project it means a Spring Boot application that holds several configured datasources and switches between them at runtime, rather than binding a single DataSource at startup. The README states the framework only performs the switching and does not restrict the CRUD you run afterwards.

### Which starter artifact should I use for Spring Boot 3 or Spring Boot 4?

Spring Boot 3.x.x uses dynamic-datasource-spring-boot3-starter and Spring Boot 4.x.x uses dynamic-datasource-spring-boot4-starter, both requiring JDK 17 or above. Spring Boot 1.5.x through 2.x.x uses dynamic-datasource-spring-boot-starter, which supports JDK 8 and above.

### What happens if a @DS name does not match any configured datasource?

With spring.datasource.dynamic.strict left at its default of false, the request falls back to the default datasource. Setting strict to true makes an unmatched name raise an exception instead, which the README presents as the strict matching option.

### Does baomidou/dynamic-datasource handle distributed transactions?

The feature list includes a Seata-based distributed transaction scheme and a local multi-datasource transaction scheme. The README lists them as provided options rather than describing them as automatic behaviour, and the details are on the documentation site.

## Sources

- [baomidou/dynamic-datasource on GitHub](https://github.com/baomidou/dynamic-datasource)
- [License: Apache-2.0](https://github.com/baomidou/dynamic-datasource/blob/master/LICENSE)
- [Project website](https://doc.xiuceyun.cn)
- [README](https://github.com/baomidou/dynamic-datasource/blob/master/README.md)
- [Releases](https://github.com/baomidou/dynamic-datasource/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/baomidou-dynamic-datasource
