I think I'm close to one solution, but I'm not too happy with that. I would like a better answer.
EDITED: It turns out that this does not quite work, since Spring or Hibernate appears only to call only the current tenant identifier identifier, and not for every call to the @Transactional method
This is due to a change in the implementation of CurrentTenantIdentifierResolver to not only look at the current user (if installed), to get his current tenant ID (before the developer, to figure out how to set it) ... he should also look at the local stream variable to Find out if an override is set.
Using this approach, I can temporarily set tenantID ... call the service method with my multi-tenant transaction manager, and then get the data.
My test service:
@Service public class TestService { @Transactional(transactionManager = "sharedTxMgr") public void doSomeGets() { List<String> tenants = getListSomehow(); try(MultitenancyTemporaryOverride tempOverride = new MultitenancyTemporaryOverride()) { for(String tenant : tenants) { tempOverride.setCurrentTenant(tenant); doTenantSpecificWork(); } } catch (Exception e) { logger.error(e); } } @Transactional(transactionManager = "tenantSpecificTxMgr") public void doTenantSpecificWork() {
My class that wraps the ThreadLocal installation by implementing AutoCloseable to make sure the variable is cleared.
public class MultitenancyTemporaryOverride implements AutoCloseable { static final ThreadLocal<String> tenantOverride = new ThreadLocal<>(); public void setCurrentTenant(String tenantId) { tenantOverride.set(tenantId); } public String getCurrentTenant() { return tenantOverride.get(); } @Override public void close() throws Exception { tenantOverride.remove(); } }
My tenant's implementation using local thread
public class CurrentTenantIdentifierResolverImpl implements CurrentTenantIdentifierResolver { @Override public String resolveCurrentTenantIdentifier() { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); logger.debug(ToStringBuilder.reflectionToString(authentication)); String database = "shared"; if (authentication != null && authentication.getPrincipal() instanceof MyUser) { MyUser user = (MyUser) authentication.getPrincipal(); database = user.getTenantId(); } String temporaryOverride = MultitenancyTemporaryOverride.tenantOverride.get(); if(temporaryOverride != null) { database = temporaryOverride; } return database; }
source share