How to use multi-datasource-utils for tenant based datasource routing
Edit me
Purpose
multi-datasource-utils gives tenant-aware datasource routing for JPA workloads.
It registers a primary routing DataSource that selects datasource from ContextContainer.getTenant().
When To Use
Use this module when:
- You run one service for multiple tenants.
- Each tenant maps to a separate datasource.
- Tenant identity is passed in request headers (
x-chenile-tenant-id).
Add Dependency
<dependency>
<groupId>org.chenile</groupId>
<artifactId>multi-datasource-utils</artifactId>
</dependency>
Configuration
Define tenant datasources under chenile.multids.
chenile:
multids:
defaultTenantId: tenant1
datasources:
tenant1:
jdbcUrl: jdbc:h2:mem:tenant1;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
username: sa
password: ''
maximumPoolSize: '5'
tenant2:
jdbcUrl: jdbc:h2:mem:tenant2;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
username: sa
password: ''
maximumPoolSize: '5'
How Routing Works
- If tenant is present in
ContextContainer, that tenant datasource is selected. - If tenant is missing or unknown, default datasource is used.
- If no default is configured, first datasource in config is used as fallback.
Request Header Contract
Pass tenant via header:
x-chenile-tenant-id: tenant1
To populate context from HTTP headers, keep Chenile preprocessor setup enabled (common setup uses populateContextContainer).
Minimal End-To-End Flow
- Configure tenant datasources in
application.yml. - Ensure controller requests flow through Chenile entrypoint (
ControllerSupport). - Send
x-chenile-tenant-idin incoming requests. - Execute repository call as usual; routing happens transparently.
Example Controller Pattern
@RestController
@ChenileController(value = "tenantItemService", serviceName = "tenantItemService")
public class TenantItemController extends ControllerSupport {
@GetMapping("/test/items")
public ResponseEntity<GenericResponse<Map<String, Object>>> items(HttpServletRequest request) {
return process(request);
}
}
Verification Pattern
BDD tests in this module verify:
- no tenant header => default tenant data
tenant1header => tenant1 datatenant2header => tenant2 data- unknown tenant => fallback to default tenant
Use this as baseline behavior for your app tests.
Common Pitfalls
- Missing
x-chenile-tenant-idin upstream gateway/service chain. - Not configuring
defaultTenantIdwhile expecting fallback. - Skipping Chenile context population in pre-processors.
- Typo in tenant keys between headers and datasource map.