How to Configure Multiple Database Sources with dynamic-datasource in ContiNew-Admin
ContiNew-Admin ships with dynamic-datasource-spring-boot-starter v4.3.1, enabling you to route database operations to different JDBC sources using YAML configuration and the @DS annotation without modifying existing DAO code.
The ContiNew-Admin repository already integrates the dynamic-datasource ecosystem, making it trivial to configure multiple database sources for read-write splitting or multi-tenant scenarios. This article explains how to configure additional data sources in the existing YAML structure and route SQL operations using declarative annotations or programmatic context switching.
How dynamic-datasource Works in ContiNew-Admin
The starter registers a DynamicRoutingDataSource bean that extends Spring's AbstractRoutingDataSource. At runtime, this bean maintains a map of named DataSource objects configured under the spring.datasource.dynamic.datasource block.
When a service method executes, the @DS annotation triggers a MethodInterceptor that pushes the specified data source key onto a thread-local DynamicDataSourceContextHolder before the method runs and clears it afterward. If no annotation is present, the framework falls back to the primary data source defined in the configuration. All existing code using JdbcTemplate, MyBatis-Plus, or JPA receives the routed connection transparently.
Step-by-Step Configuration Guide
Verify the Starter Dependency
The dynamic-datasource-spring-boot-starter is already declared in the parent POM as part of the core technology stack. You do not need to add additional dependencies. According to the repository documentation in README.md (line 248), version 4.3.1 is the current bundled release.
Define Data Sources in YAML
Extend the existing configuration in continew-server/src/main/resources/config/application-dev.yml (or application-prod.yml) with a dynamic block under spring.datasource. The primary key designates the default data source, while the datasource map contains named entries for each connection pool.
spring:
datasource:
# Existing primary configuration
type: com.zaxxer.hikari.HikariDataSource
url: jdbc:p6spy:mysql://${DB_HOST:127.0.0.1}:${DB_PORT:3306}/${DB_NAME:continew_admin}
username: ${DB_USER:root}
password: ${DB_PWD:123456}
driver-class-name: com.p6spy.engine.spy.P6SpyDriver
# Dynamic multi-source configuration
dynamic:
primary: master
datasource:
master:
url: jdbc:p6spy:mysql://${DB_HOST:127.0.0.1}:${DB_PORT:3306}/${DB_NAME:continew_admin}
username: ${DB_USER:root}
password: ${DB_PWD:123456}
driver-class-name: com.p6spy.engine.spy.P6SpyDriver
hikari:
maximum-pool-size: 20
slave:
url: jdbc:p6spy:mysql://${SLAVE_DB_HOST:127.0.0.1}:${SLAVE_DB_PORT:3306}/${SLAVE_DB_NAME:continew_admin_slave}
username: ${SLAVE_DB_USER:root}
password: ${SLAVE_DB_PWD:123456}
driver-class-name: com.p6spy.engine.spy.P6SpyDriver
hikari:
maximum-pool-size: 10
Each named entry supports standard HikariCP properties under the hikari key, allowing independent pool sizing for different database workloads.
Annotate Service Methods with @DS
Import com.baomidou.dynamic.datasource.annotation.DS and apply it to service methods or classes that must execute against a non-primary source. For example, in continew-system/src/main/java/top/continew/admin/system/service/impl/DeptServiceImpl.java, you can route read-only operations to the slave database:
package top.continew.admin.system.service.impl;
import com.baomidou.dynamic.datasource.annotation.DS;
import org.springframework.stereotype.Service;
import top.continew.admin.system.service.DeptService;
@Service
public class DeptServiceImpl implements DeptService {
@Override
@DS("slave")
public List<DeptVO> listAll() {
// Executes against the slave data source
return deptMapper.selectList(null);
}
@Override
public void create(DeptCreateReq req) {
// No annotation: uses the primary (master) data source
deptMapper.insert(req);
}
}
When placed at the class level, @DS applies to all public methods in that bean. Method-level annotations override class-level declarations.
Programmatic Data Source Switching
For scenarios requiring runtime decision logic, use DynamicDataSourceContextHolder to push and pop data source keys manually. Always wrap the operation in a try-finally block to ensure the context is cleared and connections are returned to the correct pool.
import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
public void exportHistoricalData() {
DynamicDataSourceContextHolder.push("slave");
try {
List<Record> rows = recordMapper.selectAll();
// Processing logic here
} finally {
DynamicDataSourceContextHolder.poll(); // Restores previous data source
}
}
This approach is useful when the target data source cannot be determined statically, such as in multi-tenant applications where the tenant identifier drives the routing decision.
Summary
- ContiNew-Admin pre-installs
dynamic-datasource-spring-boot-starterv4.3.1; no additional Maven dependencies are required. - Configure multiple sources under
spring.datasource.dynamic.datasourceinapplication-dev.ymlorapplication-prod.yml, specifying aprimarydefault. - Use the
@DS("name")annotation fromcom.baomidou.dynamic.datasource.annotation.DSon service methods to route queries to specific databases. - Implement programmatic switching with
DynamicDataSourceContextHolder.push()andpoll()for dynamic runtime routing. - Existing MyBatis-Plus mappers and JDBC templates work without modification because the routing happens at the
DataSourceproxy level.
Frequently Asked Questions
Is dynamic-datasource already included in ContiNew-Admin?
Yes. The starter is listed in the Core Technology Stack table in README.md and is inherited by all modules through the parent POM. You can begin configuring additional sources immediately without adding new dependencies to your pom.xml.
How do I switch data sources without using annotations?
Use DynamicDataSourceContextHolder.push("datasourceName") to set the current thread's data source key before executing database operations. Call DynamicDataSourceContextHolder.poll() in a finally block to restore the previous context. This is implemented in the com.baomidou.dynamic.datasource.toolkit package provided by the starter.
Can I configure more than two data sources?
Absolutely. The spring.datasource.dynamic.datasource map accepts any number of named entries. Simply add additional keys (e.g., tenant_a, tenant_b, reporting_db) with their respective JDBC URLs and credentials. Each operates as an independent HikariCP pool managed by the DynamicRoutingDataSource.
Does this integration work with MyBatis-Plus and JPA?
Yes. The routing logic sits at the DataSource level, below the ORM layer. Whether you use MyBatis-Plus mappers (as seen in DeptServiceImpl.java), Spring Data JPA repositories, or raw JdbcTemplate, all database connections are automatically routed according to the current context holder key or @DS annotation.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →