I am trying to understand the difference between R2DBC EntityCallbacks and R2DBC Mapping. I am trying to use it in a Multitenant application, where I would like to map the TenantId using a web filter and request context holder to set it before making the DB call.
In the above context which will be the better approach so that handling the tenant id is separated from the business logic for the current endpoint?
For database/R2dbc multitenant support, there is an AbstractRoutingConnectionFactory
which can be used to switch the certain tenant database from the client request.
public class TenantAwareConnectionFactory extends AbstractRoutingConnectionFactory {
@Override
protected Mono<Object> determineCurrentLookupKey() {
return CurrentTenantIdHolder.getTenantId().map(id -> (Object) id);
}
}
We use CurrentTenantIdHolder
to hold the tenant id from request context.
public class CurrentTenantIdHolder {
public static final String TENANT_ID = CurrentTenantIdHolder.class.getName() + ".TENANT_ID";
public static Context withTenantId(String id) {
return Context.of(TENANT_ID, Mono.just(id));
}
public static Mono<String> getTenantId() {
return Mono.deferContextual(contextView -> {
if (contextView.hasKey(TENANT_ID)) {
return contextView.get(TENANT_ID);
}
return Mono.empty();
}
);
}
public static Function<Context, Context> clearContext() {
return (context) -> context.delete(TENANT_ID);
}
}
Use a filter to set up the incoming tenant id.
@Component
public class TenantFilter implements WebFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
var value = exchange.getRequest().getHeaders().getFirst("X-Tenant-Id");
if (StringUtils.hasText(value)) {
return chain.filter(exchange)
.contextWrite(CurrentTenantIdHolder.withTenantId(value));
}
return chain.filter(exchange);
}
}
Here I assume you are using a header X-Tenant-Id
to identify the incoming tenant. You can use query parameter, or sub domain, etc.
Check the complete example here: https://github.com/hantsy/spring-puzzles/blob/master/multi-tenancy-r2dbc/