Eclipse Community Forums
Forum Search:

Search      Help    Register    Login    Home
Home » Eclipse Projects » EclipseLink » Multi tenancy with schema per tenant
Multi tenancy with schema per tenant [message #1480947] Thu, 20 November 2014 16:20 Go to next message
Martin Frano is currently offline Martin FranoFriend
Messages: 6
Registered: November 2014
Junior Member
Hello,

I am tryign to run multi tenancy with schema per tenant. It seems to work good when tenant ID is set in XML as a property for persistance unit.
But I need to set tenant in code. Is that possible? How?
I found this article:
codecrafters.blogspot.it/2013/03/multi-tenant-cloud-applications-with.html

So I tried to set it in doBegin method in custom transaction manager. But that doesn't work. I guess relation descriptors aren't updated right.
I could provide more info with exception stack also but first... have I pick the right approach?
Thanks!

Martin
Re: Multi tenancy with schema per tenant [message #1482115 is a reply to message #1480947] Fri, 21 November 2014 13:57 Go to previous messageGo to next message
Mauro Molinari is currently offline Mauro MolinariFriend
Messages: 285
Registered: July 2009
Senior Member
Hi Martin,
how are you approaching this problem (schema per tenant)? EclipseLink seems to support multi-tenancy with single table or table-per-tenant, but not schema per tenant.
I'm also trying to build a solution for this during these days and I'm following the route to use partitioning to provide a different data source for each tenant, together with a suitable PartitioningPolicy that determines what the current tenant is.
I'm at a good point, what I'm trying to do now is to find a way to make the shared cache work (partitioning assumes any entity with id X to be the same in all partitions where it is located, while I would need entity with id=X for tenant 1 to be different from entity with id=X for tenant 2).

The article you're linking explicitly talks about the single-table strategy for multi-tenancy...
Re: Multi tenancy with schema per tenant [message #1482169 is a reply to message #1482115] Fri, 21 November 2014 14:53 Go to previous messageGo to next message
Mauro Molinari is currently offline Mauro MolinariFriend
Messages: 285
Registered: July 2009
Senior Member
II jsut saw that the table-per-tenant strategy can be configured actually as a schema-per-tenant strategy by specifying SCHEMA as the TenantTableDiscriminator, I should have read better this... however your message and the one in the comments of the article you've linked makes me suspect this does not work well...

On the contrary, the approach I'm following is promising, I just have to find a solution for the shared cache problem. In theory it should be possible to make EclipseLink use a separate datasource for sequences: this, combined with table sequence generation should let me assign distinct ids to records in distinct partitions and hence should give no problems to the shared cache... However I found out that when a partition policy is in use, the sequence connection pool seems to be completely ignored...
Re: Multi tenancy with schema per tenant [message #1482313 is a reply to message #1482169] Fri, 21 November 2014 17:29 Go to previous messageGo to next message
Martin Frano is currently offline Martin FranoFriend
Messages: 6
Registered: November 2014
Junior Member
Hi,
the last comment in the linked article is mine... but anyway it doesn't work well for schema-per-tenant. If you will find some way to make it working I would be glad if you write it here.
Thanks!
Re: Multi tenancy with schema per tenant [message #1489636 is a reply to message #1482313] Thu, 27 November 2014 14:48 Go to previous messageGo to next message
Martin Frano is currently offline Martin FranoFriend
Messages: 6
Registered: November 2014
Junior Member
UPDATE 26.6.2015: This isn't completelly right solution. Read also comments below or go to my article at http://www.mafospace.com/articles/multi-tenancy-with-eclipselink-and-inherited-entities

I've solved it finally. Maybe it will be helpful for someone. I've done that in similar way as described in article I linked in first post. Through custom transaction manager.

import org.eclipse.persistence.config.PersistenceUnitProperties;
import org.eclipse.persistence.internal.jpa.EntityManagerImpl;
import org.eclipse.persistence.sessions.coordination.MetadataRefreshListener;
import org.eclipse.persistence.sessions.server.ServerSession;
import org.springframework.orm.jpa.JpaTransactionManager;
import sk.bantip.hotel.server.security.SecurityHelper;

import javax.persistence.EntityManager;
import java.util.HashMap;
import java.util.Map;

public class MultiTenantJpaTransactionManager extends JpaTransactionManager {

    /**
     * NOTE:
     * Maybe it would be also possible to replace existing entityManager in transaction with new but it
     * isn't a good idea because of rollback and other problems.
     * So when new tenant is required always start new transaction for it.
     */

    @Override
    protected javax.persistence.EntityManager createEntityManagerForTransaction() {
        EntityManager em = super.createEntityManagerForTransaction();

        boolean refreshed = false;
        String actualTenant = null;
        ServerSession ss = ((EntityManagerImpl) em.getDelegate()).getServerSession();
        Map sessionProp = ss.getProperties();
        actualTenant = (String) sessionProp.get(PersistenceUnitProperties.MULTITENANT_PROPERTY_DEFAULT);
        // don't run it if tenant didn't change
        // it should be quite faster then
        if ((actualTenant == null && SecurityHelper.getActiveTenantSchema() != null) ||
                (actualTenant != null  && !actualTenant.equals(SecurityHelper.getActiveTenantSchema()))) {
            // set new tenant as property for actual session
            // while refreshing metadata it will be used from actual session for new session
            sessionProp.put(PersistenceUnitProperties.MULTITENANT_PROPERTY_DEFAULT, SecurityHelper.getActiveTenantSchema());
            MetadataRefreshListener mrl = ((EntityManagerImpl) em.getDelegate()).getServerSession().getRefreshMetadataListener();
            // metadata refresh listener is empty if it was already run for actual transaction (same entity manager)
            // because it is placed in createEntityManagerForTransaction now this shouldn't happen but to be sure...
            if (mrl != null) {
                Map<String, Object> prop = new HashMap<String, Object>();
                // metadata will be refreshed for next created entity manager
                mrl.triggerMetadataRefresh(prop);
                refreshed = true;
            }
        }

        // if metadata for "old" entity manager wasn't refreshed we don't need to create a new one
        return refreshed ? super.createEntityManagerForTransaction() : em;
    }
}


Everything seems to work except inheritance with strategy type JOINED now. To fix that use customizer.

import org.eclipse.persistence.annotations.TenantTableDiscriminatorType;
import org.eclipse.persistence.config.DescriptorCustomizer;
import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.descriptors.TablePerMultitenantPolicy;

/**
 * For some reason when using table per tenant with schema discriminator isn't set good for child
 * entities with inheritance JOINED strategy. It stay as SUFFIX and therefore it doesn't work.
 */
public class InheritanceJoinedMTFixCustomizer implements DescriptorCustomizer {

    @Override
    public void customize(ClassDescriptor descriptor) throws Exception {
        // set discriminator to SCHEMA
        ((TablePerMultitenantPolicy) descriptor.getMultitenantPolicy())
                .setTenantTableDiscriminatorType(TenantTableDiscriminatorType.SCHEMA);
    }
}


And in child entities use it like this:

...
@Customizer(InheritanceJoinedMTFixCustomizer.class)
public class Person extends Contact {
...

[Updated on: Fri, 26 June 2015 11:15]

Report message to a moderator

Re: Multi tenancy with schema per tenant [message #1622138 is a reply to message #1489636] Wed, 18 February 2015 09:36 Go to previous messageGo to next message
Mauro Molinari is currently offline Mauro MolinariFriend
Messages: 285
Registered: July 2009
Senior Member
Thanks Martin for sharing your solution to make the schema-per-tenant solution work. I saved this discussion for future reference.

Anyway, I went ahead with my idea to rather use partitioning to implement datasource-per-tenant multi tenancy and I got it working. You can find my solution on StackOverflow:
http://stackoverflow.com/a/28580072/1465635

Mauro
Re: Multi tenancy with schema per tenant [message #1690135 is a reply to message #1480947] Wed, 25 March 2015 12:53 Go to previous messageGo to next message
Felipe Ferreira do Amaral is currently offline Felipe Ferreira do AmaralFriend
Messages: 1
Registered: March 2015
Junior Member
Sorry guys for follow up this topic, but I need a little help.

I'm using the Martin Franco solution to implement tenancy in my application and I have some problems now with that.

I posted in the StackOverflow and don't get a good answer about that:
eclipselink-error-attempted-to-redeploy-a-session-without-closing-it

My problem is, after some time running (about 2 hours) it's return error of redeploy a session without closing it.

org.springframework.transaction.CannotCreateTransactionException: Could not open JPA EntityManager for transaction; nested exception is javax.persistence.PersistenceException: Exception [EclipseLink-28013] (Eclipse Persistence Services - 2.5.2.v20140319-9ad6abd): org.eclipse.persistence.exceptions.EntityManagerSetupException Exception Description: Unable to deploy PersistenceUnit ###### in invalid state [DeployFailed]. Internal Exception: javax.persistence.PersistenceException: Exception [EclipseLink-28019] (Eclipse Persistence Services - 2.5.2.v20140319-9ad6abd): org.eclipse.persistence.exceptions.EntityManagerSetupException Exception Description: Deployment of PersistenceUnit ###### failed. Close all factories for this PersistenceUnit. Internal Exception: javax.persistence.PersistenceException: Exception [EclipseLink-28009] (Eclipse Persistence Services - 2.5.2.v20140319-9ad6abd): org.eclipse.persistence.exceptions.EntityManagerSetupException Exception Description: Attempted to redeploy a session named file: ####### without closing it.


Unfortunately this error happens only when using the class MultiTenantJpaTransactionManager that extends JpaTransactionManager.

Someone has gone through this problem?

Thanks guys.
Re: Multi tenancy with schema per tenant [message #1698094 is a reply to message #1690135] Thu, 11 June 2015 07:50 Go to previous messageGo to next message
Martin Frano is currently offline Martin FranoFriend
Messages: 6
Registered: November 2014
Junior Member
I haven't seen such problem yet in my application but have encountered another one when concurrent requests are placed on application. I am still not sure what exactly is happening but seems like my solution isn't completely allright. Shame I still can't find a better one... If I will come up with something new I will write it here.
Re: Multi tenancy with schema per tenant [message #1699753 is a reply to message #1698094] Fri, 26 June 2015 11:13 Go to previous messageGo to next message
Martin Frano is currently offline Martin FranoFriend
Messages: 6
Registered: November 2014
Junior Member
Felipe Ferreira do Amaral, I have already also noticed exception you mentioned. I have tweaked my previous solution and a new seems to work (I hope it really is):

Class MultiTenantJpaTransactionManager:

public class MultiTenantJpaTransactionManager extends JpaTransactionManager {

  /**
   * This process shouldn't be interrupted by concurrent thread.
   * Therefore the method is synchronized.
   */
  @Override
  protected synchronized EntityManager createEntityManagerForTransaction() {
    EntityManager em = null;
    Map<String, Object> properties = getJpaPropertyMap();
    // get EMF from JpaTransactionManager
    EntityManagerFactory emf = ((EntityManagerFactoryInfo) getEntityManagerFactory()).getNativeEntityManagerFactory();
    boolean isMetadataExpired = ((EntityManagerFactoryImpl) emf).unwrap().getSetupImpl().isMetadataExpired();
    // it is needed to get EM to update metadata if they are marked as expired otherwise serverSesstion and
    // therefore also actualTenant value being get below wouldn't be actual
    if (isMetadataExpired) {
      em = (!CollectionUtils.isEmpty(properties) ?
           emf.createEntityManager(properties) : emf.createEntityManager());
    }
    Server ss = JpaHelper.getServerSession(emf);
    String actualTenant = (String) ss.getProperty(PersistenceUnitProperties.MULTITENANT_PROPERTY_DEFAULT);
    // don't run it if tenant didn't change
    // it should be quite faster then
    if ((actualTenant == null && SecurityHelper.getActiveTenantSchema() != null) ||
        (actualTenant != null  && !actualTenant.equals(SecurityHelper.getActiveTenantSchema()))) {
      Map newProperties = new HashMap();
      newProperties.put(PersistenceUnitProperties.MULTITENANT_PROPERTY_DEFAULT, SecurityHelper.getActiveTenantSchema());
      JpaHelper.getEntityManagerFactory(emf).refreshMetadata(newProperties);
    } else
    if (em != null) {
      // don't get it again
      // it is unnecessary
      return em;
    }

    return (!CollectionUtils.isEmpty(properties) ? emf.createEntityManager(properties) : emf.createEntityManager());
  }
}


For inherited entities with InheritanceType.JOINED you would also need to add @Multitenant annotation to child entities and use customizer I have already mentioned in comments above.
Re: Multi tenancy with schema per tenant [message #1699754 is a reply to message #1699753] Fri, 26 June 2015 11:14 Go to previous messageGo to next message
Martin Frano is currently offline Martin FranoFriend
Messages: 6
Registered: November 2014
Junior Member
If you have time you can read whole article at http://www.mafospace.com/articles/multi-tenancy-with-eclipselink-and-inherited-entities
Re: Multi tenancy with schema per tenant [message #1764373 is a reply to message #1699754] Mon, 29 May 2017 09:36 Go to previous messageGo to next message
John N is currently offline John NFriend
Messages: 4
Registered: May 2017
Junior Member
Have a concurrency issue in Table per Tenant

stackoverflow.com/questions/44153952/concurrency-not-handled-in-eclipse-link-when-using-spring-jpatransactionmanager

Is this still a know Issue?
Re: Multi tenancy with schema per tenant [message #1764594 is a reply to message #1764373] Wed, 31 May 2017 14:38 Go to previous messageGo to next message
Chris Delahunt is currently offline Chris DelahuntFriend
Messages: 1275
Registered: July 2009
Senior Member
Schema per tenant should not involve having to change the MULTITENANT_PROPERTY_DEFAULT within the server session or call refreshMetadata - this causes you to rebuild the entire shared server session each time you need an EntityManager. Using EclpseLink multitenant table/tenant or schema/tenant had some flaws in how queries were generated, but it was working for me using the workaround outlined here: https://bugs.eclipse.org/bugs/show_bug.cgi?id=418170.

In the end, I went with building a tenant/EMF solution and caching the EMFs by tenant. This just requires naming the sessions uniquely with https://www.eclipse.org/eclipselink/documentation/2.4/jpa/extensions/p_session_name.htm , possibly by using the tenant name with the persistence name. It also means handling the EMF lifecycle yourself and makes cache coordination at bit more tricky, but with multitenant solutions you can't use the shared server session level cache anyway.

Re: Multi tenancy with schema per tenant [message #1764979 is a reply to message #1764594] Mon, 05 June 2017 11:31 Go to previous messageGo to next message
John N is currently offline John NFriend
Messages: 4
Registered: May 2017
Junior Member
The work around link is not accessible. Can you provide some other references?
Re: Multi tenancy with schema per tenant [message #1765440 is a reply to message #1764979] Fri, 09 June 2017 13:45 Go to previous messageGo to next message
Chris Delahunt is currently offline Chris DelahuntFriend
Messages: 1275
Registered: July 2009
Senior Member
just remove the period at the end https://bugs.eclipse.org/bugs/show_bug.cgi?id=418170
Re: Multi tenancy with schema per tenant [message #1774259 is a reply to message #1765440] Thu, 12 October 2017 10:19 Go to previous messageGo to next message
John N is currently offline John NFriend
Messages: 4
Registered: May 2017
Junior Member
In the code given by Martin I am trying to use the same but I am unable to import the calss SecurityHelper, Please tell me is it your custom class? If so can you provide its implementation?
Re: Multi tenancy with schema per tenant [message #1774591 is a reply to message #1774259] Tue, 17 October 2017 15:17 Go to previous message
Chris Delahunt is currently offline Chris DelahuntFriend
Messages: 1275
Registered: July 2009
Senior Member
The value expected is your tenant schema, so it would have to be your own implementation. But I'll jump in and say again this code is not what you should be using. RefreshMetadata is not meant for multi tenancy and while its side effect can 'work', there are other, more efficient ways to get the same behaviour. RefreshMetatadata entirely rebuilds the persistence context under the covers, scanning all the files associated with the context, and duplicating it. Unless this metadata has changed, this process is entirely unnecessary. EclipseLink multi tenancy options were created in such a way that all you need to do is pass the tenant specific property in to the get factory or entityManager methods.
Previous Topic:JPA - String attribute linked to xml type column
Next Topic:complex merge fails with IntegrityConstraintException <SOLVED>
Goto Forum:
  


Current Time: Fri Dec 15 23:43:25 GMT 2017

Powered by FUDForum. Page generated in 0.03112 seconds
.:: Contact :: Home ::.

Powered by: FUDforum 3.0.2.
Copyright ©2001-2010 FUDforum Bulletin Board Software