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

Popular posts from this blog

javascript - gulp-nodemon - nodejs restart after file change - Error: listen EADDRINUSE events.js:85 -

Fatal Python error: Py_Initialize: unable to load the file system codec. ImportError: No module named 'encodings' -

javascript - oscilloscope of speaker input stops rendering after a few seconds -