Question

I am using EclipseLink for Single Table Multi-Tenancy.

I have annotated entities with @Multi-Tenant and everything is working fine.

Is it possible to turn off Multi-Tenancy without any change?.

I know there is a org.eclipse.persistence.annotations.Multitenant.includeCriteria() which I can set turn off to disable Multi-tenancy.

But my solution is packaged and delivered as a Package mode and there is no any way to set above attribute to false.

What I want, even I annotated Entity with @Multi-Tenant annotation and even there is a column TENANT_ID in database, but if I does not set PersistenceUnitProperties.MULTITENANT_PROPERTY_DEFAULT than it should not throw below exception and any CRUD operation should be succeed.

    javax.persistence.PersistenceException: Exception [EclipseLink-6174] (Eclipse Persistence Services - 2.5.1.v20130918-f2b9fc5): org.eclipse.persistence.exceptions.QueryException
Exception Description: No value was provided for the session property [eclipselink.tenant-id]. This exception is possible when using additional criteria or tenant discriminator columns without specifying the associated contextual property. These properties must be set through Entity Manager, Entity Manager Factory or persistence unit properties. If using native EclipseLink, these properties should be set directly on the session.

So, is there any way or any customization/extension point(s) so that If I set

PersistenceUnitProperties.MULTITENANT_PROPERTY_DEFAULT 

then It should include TENANT_ID as an criteria and if I not set than simply it should not include TENANT_ID as an criteria?

Was it helpful?

Solution

This seems to be an open issue for eclipselink. I have posted a similar question lately. Chris posted an helpful answer with a link to a feature request. A workaround described there is the usage of multiple persistence units for the same tables with different multi tenancy settings.

I am trying to use an alternative approach: Instead of @Multitenant I use @AdditionalCriteria. This annotations enables you to define your own request criteria added to any database operation (with few exceptions like native SQL).

Example:

@AdditionalCriteria(":ADMINACCESS = 1 or this.tenant=:TENANT")

On creation of your entity manager you set the parameters. You could either provide a tenant of switch off multi tenancy. The drawback here is that you are responsible for the tenant attribute.

OTHER TIPS

There is another workaround to this problem. You can use an Eclipselink MetadataSource to override at runtime the annotations in the entity class and to pretend that you use a dedicated database per tenant, for example:

  MultitenantMetadata md = new MultitenantMetadata();
  md.setIncludeCriteria(false);
  md.setType(MultitenantType.TABLE_PER_TENANT.name());
  TenantTableDiscriminatorMetadata td = new TenantTableDiscriminatorMetadata();
  td.setType(TenantTableDiscriminatorType.SCHEMA.toString());
  td.setContextProperty(DISCRIMINATOR_SCHEMA_PROPERTY);
  md.setTenantTableDiscriminator(td);
  entityAccessor.setMultitenant(md);

The above will make Eclipselink happy from a multi-tenancy point of view, but your tables may still have the discriminator column if your annotations specify that multi-tenancy is achieved using a discriminator column.

To remove the discriminator column you can use a SessionCustomizer that manipulates the EclipseLink representation of the entities before the EMF is created in order to:

  • Remove the discriminator column
  • Change indices and foreign keys to remove any reference to the discriminator column.

In this way the database tables and the DDLs will no more include the discriminator column.

        Set<String> discriminatorColumnNames = getDiscriminatorColumns(descriptor);

        for (DatabaseTable databaseTable : descriptor.getTables()) {
            List<IndexDefinition> indexDefinitions = databaseTable.getIndexes();
            for (Iterator<IndexDefinition> definitionIt = indexDefinitions.iterator(); definitionIt.hasNext();) {
                IndexDefinition indexDefinition = definitionIt.next();
                // remove qualifier from index to prevent a syntax error in DDL
                indexDefinition.setQualifier("");
                for (Iterator<String> fieldNameIt = indexDefinition.getFields().iterator(); fieldNameIt.hasNext();) {
                    if (discriminatorColumnNames.contains(fieldNameIt.next().toUpperCase())) {
                        fieldNameIt.remove();
                    }
                }
                if (indexDefinition.getFields().isEmpty()) {
                    definitionIt.remove();
                }
            }

            Map<String, List<List<String>>> uniqueConstraints = databaseTable.getUniqueConstraints();
            for (Entry<String, List<List<String>>> uc : uniqueConstraints.entrySet()) {
                List<List<String>> strings = uc.getValue();
                for (List<String> list : strings) {
                    String found = null;
                    for (String string : list) {
                        if (discriminatorColumnNames.contains(string.toUpperCase())) {
                            found = string;
                            break;
                        }
                    }
                    if (found != null) {
                        list.remove(found);
                    }
                }

            }

        }
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top