java - Hibernate multitenancy: change tenant in session -
we're developing saas solution several consumers. solution based on spring, wicket , hibernate. our database contains data several customers. we've decided model database follows:
- public
shared data between customers, example user accounts not know customer user belongs to - customer_1
- customer_2
- ...
to work setup use multi-tenancy setup following tenantidentifierresolver:
public class tenantproviderimpl implements currenttenantidentifierresolver { private static final threadlocal<string> tenant = new threadlocal<>(); public static void settenant(string tenant){ tenantproviderimpl.tenant.set(tenant); } @override public string resolvecurrenttenantidentifier() { return tenant.get(); } @override public boolean validateexistingcurrentsessions() { return false; } /** * initialize tenant storing tenant identifier in both http session , threadlocal * * @param string tenant tenant identifier stored */ public static void inittenant(string tenant) { httpservletrequest req = ((servletwebrequest) requestcycle.get().getrequest()).getcontainerrequest(); req.getsession().setattribute("tenant", tenant); tenantproviderimpl.settenant(tenant); } }
the inittenant
method called servlet filter every request. filter processed before connection opened database.
we've implemented abstractdatasourcebasedmultitenantconnectionproviderimpl
set our hibernate.multi_tenant_connection_provider
. issues set search_path
query before every request. works charm requests passing through servlet filter described above.
and our real problem: we've got entrypoints our application not pass servlet filter, instance soap-endpoints. there timed jobs executed not pass servlet filter. proves problem.
the job/endpoint receives value somehow can used identify customer should associated job/endpoint-request. unique value mapped in our public
database schema. thus, need query database before know customer associated. spring therefore initializes complete hibernate session. session has our default tenant id , not mapped specific customer. however, after we've resolved unique value customer want session change tenant identifier. seems not supported though, there no such thing hibernatesession.settenantidentifier(string)
whereas there sharedsessioncontract.gettenantidentifier()
.
we thought had solution in following method:
org.hibernate.sessionfactory sessionfactory = getsessionfactory(); org.hibernate.session session = null; try { session = getsession(); if (session != null) { if(session.isdirty()) { session.flush(); } if(!session.gettransaction().wascommitted()) { session.gettransaction().commit(); } session.disconnect(); session.close(); transactionsynchronizationmanager.unbindresource(sessionfactory); } } catch (hibernateexception e) { // no-op, apparently there no session yet } tenantproviderimpl.settenant(tenant); session = sessionfactory.opensession(); transactionsynchronizationmanager.bindresource(sessionfactory, new sessionholder(session)); return session;
this method not seem work in context of job/endpoint , leads hibernateexception
such "session closed!" or "transaction not succesfully started".
we're bit lost we've been trying find solution quite while now. there we've misunderstood? we've misinterpreted? how can fix problem above?
recap: hibernatesession
-s not created user request rather timed job or such not pass our servlet filter , have no associated tenant identifier before hibernate session started. have unique values can translate tenant identifier querying database though. how can tell existing hibernate session alter it's tenant identifier , issue new set search_path
statement?
we've never found true solution problem, chimmi linked jira-ticket others have requested such feature: https://hibernate.atlassian.net/browse/hhh-9766
as per ticket, behavior want unsupported. we've found workaround though, number of times want use feature limited feasible run these operations in separate threads using default java concurrency implementation.
by running operation in separate thread, new session created (as session threadbound). important set tenant variable shared across threads. have static variable in currenttenantidentifierresolver.
for running operation in separate thread, implement callable
. these callables implemented spring-beans scope prototype
new instance created each time requested (autowired). we've implemented our own abstract implementation of callable
finalizes call()
-method defined callable
interface, , implementation starts new hibernatesession. code looks this:
public abstract class ourcallable<type> implements callable<type> { private final string tenantid; @autowired private sessionfactory sessionfactory; // more fields here public ourcallable(string tenantid) { this.tenantid = tenantid; } @override public final type call() throws exception { tenantprovider.settenant(tenantid); startsession(); try { return callinternal(); } { stopsession(); } } protected abstract type callinternal(); private void startsession(){ // implementation skipped clarity } private void stopsession(){ // implementation skipped clarity } }
Comments
Post a Comment