Skip to main content

Notice: this Wiki will be going read only early in 2024 and edits will no longer be possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

Difference between revisions of "EclipseLink/DesignDocs/Multi-Tenancy/TablePerTenant"

(Core/Runtime Exceptions)
(Core)
Line 252: Line 252:
 
== Core ==
 
== Core ==
  
A table per tenant policy will be set per individual entity.
+
A table per tenant policy will be set per individual entity. This policy is responsible for the initialization of table per tenant descriptors and the table translation name translation per tenant.
 +
 
 +
Class descriptor will need its clone method expanded to include any missing parts not already included in that method.
  
 
Server session:
 
Server session:
 
* Will contain the metadata processor produced class descriptor.
 
* Will contain the metadata processor produced class descriptor.
 +
* These descriptors are available from the existing descriptor maps and list and will also be paired out to their own list (for ease of manipulation)
 
* Unless the table per tenant property is set at the EMF level, table per tenant descriptors will never be initialized. Instead the table per tenant descriptors will cloned for each client session, updated with the table per tenant context property and those cloned descriptor are then initialized and used for the lifespan of that client session.
 
* Unless the table per tenant property is set at the EMF level, table per tenant descriptors will never be initialized. Instead the table per tenant descriptors will cloned for each client session, updated with the table per tenant context property and those cloned descriptor are then initialized and used for the lifespan of that client session.
* When the table per tenant is provided at the EMF level, the server session descriptors are fully initialized and never cloned per client session. Normal initializing operations will occur in this case.
+
* When the table per tenant is provided at the EMF level, the server session descriptors are fully initialized and never cloned per client session. Normal initializing operations will occur in this case. Setting a table per tenant at the EM level here is not allowed and an exception will be thrown.
  
 
The meat of a schema table per tenant strategy will be achieved through database level settings and access provisions. The table per tenant property will correlate directly with EclipseLink's descriptor table qualifier (which is added to all tables relating to that descriptor, table and secondary table).  
 
The meat of a schema table per tenant strategy will be achieved through database level settings and access provisions. The table per tenant property will correlate directly with EclipseLink's descriptor table qualifier (which is added to all tables relating to that descriptor, table and secondary table).  
Line 265: Line 268:
 
Under the same assumption, element collection tables should also have the table per tenant identifier as well, but again not necessary when unique id's are employed across all tenants.
 
Under the same assumption, element collection tables should also have the table per tenant identifier as well, but again not necessary when unique id's are employed across all tenants.
  
In the case of a prefix or suffix solution, instead of setting a table qualifier as described above, all table names from the cloned descriptor and its mappings will need to be updated to the new table name employing the table per tenant context.
+
In the case of a prefix or suffix solution, instead of setting a table qualifier as described above, all table names from the cloned descriptor and its mappings will need to be updated to the new table name employing the table per tenant context. This name translation will be done by the new table per tenant policy.
  
 
==== Core/Runtime Exceptions ====
 
==== Core/Runtime Exceptions ====

Revision as of 09:40, 24 April 2012

This feature will be a continuation of the multitenant feature that currently offers a SINGLE_TABLE or VPD type, see Multitenant

Table per tenant

The goal of this feature is to allow multiple application tenants to have their own individual table(s) per tenant. Table per tenant entities can be mixed with other multitenant type entities within the same persistence unit.

Requirements

  1. Support configuration of table per tenant entity types using EclipseLink specific annotations and/or eclipselink-orm.xml with the XML overriding the annotation.
  2. Ensure all READ, INSERT, UPDATE, DELETE operations populate and limit their effect to the defined table per tenant table(s)
  3. Tenants by default will share the same server session (table per tenant identifier must be updated/set per entity manager)
    1. Id generation is assumed to be unique across all table per tenants. @See Future Section.
  4. Tenant tables can be isolated by schema or a uniqe name (with a prefix or suffix on the table names).

Not supported:

  1. Schema generation will not be supported since it requires knowledge of all the tenants (schema's) and further to that, access provision must be set once the tables are created if using schema level table per tenant.

Metadata Configuration

This document will focus only on the TABLE_PER_TENANT multi-tenant type. The type will enable individual tenant table(s) usage at the entity level using a tenant context property that must be provided by the user on each entity manager after a transaction has started.

  • The TABLE_PER_TENANT states that the table(s) (Table and SecondaryTable) for the given entity are individual tenant tables based on the tenant context.
    • Relationships within that entity that use a join or collection table are also assumed to exist within the table per tenant context.
  • A TABLE_PER_TENANT type is used in conjunction with:
    • A tenant table discriminator that specifies the type of discriminator (schema or name with prefix or suffix)
    • A table per tenant property to identify the user (set per EM or at the EMF if isolating table per tenant per persistence unit)

Important notes

  • Multi-tenant metadata can only be applied at the root level of the inheritance hierarchy when using a SINGLE_TABLE or JOINED inheritance strategy. A log warning will be issued otherwise.
  • It is possible to specify multi-tenant metadata within a TABLE_PER_CLASS inheritance hierarchy.

Short example

@Entity
@Table(name=“EMP”)
@Multitenant(TABLE_PER_TENANT)
@TenantTableDiscriminator(SCHEMA)
public class Employee {
    ...
}
 
<entity class="Employee">
  <multitenant type="TABLE_PER_TENANT">
    <tenant-table-discriminator type="SCHEMA"/>
  </multitenant>
  <table name="EMP">
  ...
</entity>

tenant1.EMP

EMP_ID VERSION F_NAME L_NAME GENDER
1 1 John Doe M

tenant2.EMP

EMP_ID VERSION F_NAME L_NAME GENDER
2 3 Jane Doe F

The following new EclipseLink metadata will be added.

Annotation usage

The new type will be added to the possible value list for the existing @Multitenant annotation. See Multinancy for more configuration documentations of this annotation.

This means we will extend the MultitenantType enum to include TABLE_PER_TENANT and add a new TenantTableDiscriminator annotation and TenantTableDiscriminatorType enum.

public enum MultitenantType {
    /**
     * Specifies that table(s) the entity maps to includes rows for multiple tenants. 
     * The tenant discriminator column(s) are used with application context values to
     * limit what a persistence context can access.
     */
    SINGLE_TABLE, 
 
    /**
     * Specifies that the DB will handle the tenant filtering on all SELECT,
     * UPDATE and DELETE queries. Using this type assumes that the platform
     * used with your persistence unit does indeed support VPD.
     * 
     * @since 2.3.1
     */
    VPD
 
    /** 
     * Specifies that different tables are used for each tenant and used in 
     * conjunction with the tenant table discriminator which describes how the 
     * tables are uniquely identified, that is, using a suffix/prefix or a 
     * separate schema.
     * 
     * @since 2.4
     */
    TABLE_PER_TENANT
}
 
@Target({TYPE}) 
@Retention(RUNTIME)
public @interface TenantTableDiscriminator {
    /**
     * (Optional) The type of tenant table discriminator to use with the tables
     * of the persistence unit.
     * Defaults to {@link TenantTableDiscriminatorType#SUFFIX TenantTableDiscriminatorType.SUFFIX}.
     */
    TenantTableDiscriminatorType type() default TenantTableDiscriminatorType.SUFFIX;
}
 
public enum TenantTableDiscriminatorType {
    /**
     * Apply the tenant table discriminator as a schema to all multitenant tables.
     * NOTE: this strategy requires appropriate database provisioning.
     */
    SCHEMA, 
 
    /**
     * Apply the tenant table discriminator as a suffix to all multitenant tables. This
     * is the default strategy.
     */
    SUFFIX, 
 
    /**
     * Apply the tenant table discriminator as a prefix to all multitenant tables.
     */
    PREFIX
}

XML usage

The multitenant-type XML element will be expanded to include the new type and a new tenant table discriminator complex type (with associated simple type enum equivalent) will be added. The new table tenant discriminator will be available from the existing multitenant complex type, within a choice with the existing tenant discriminator column element.

<xsd:simpleType name="multitenant-type">
  <xsd:restriction base="xsd:token">
    <xsd:enumeration value="SINGLE_TABLE"/>
    <xsd:enumeration value="VPD"/>
    <xsd:enumeration value="TABLE_PER_TENANT"/>
  </xsd:restriction>
</xsd:simpleType>
 
<xsd:complexType name="tenant-table-discriminator">
  <xsd:attribute name="type" type="orm:tenant-table-discriminator-type"/>
</xsd:complexType>
 
<xsd:simpleType name="tenant-table-discriminator-type">
  <xsd:restriction base="xsd:token">
    <xsd:enumeration value="SCHEMA"/>
    <xsd:enumeration value="SUFFIX"/>
    <xsd:enumeration value="PREFIX"/>
  </xsd:restriction>
</xsd:simpleType>
 
<xsd:complexType name="multitenant">
  <xsd:sequence>
    <xsd:choice>
      <xsd:element name="tenant-discriminator-column" type="orm:tenant-discriminator-column" minOccurs="0" maxOccurs="unbounded"/>
      <xsd:element name="tenant-table-discriminator" type="orm:tenant-table-discriminator" minOccurs="0"/>
    </xsd:choice>
  </xsd:sequence>
  <xsd:attribute name="type" type="orm:multitenant-type"/>
  <xsd:attribute name="include-criteria" type="xsd:boolean"/>
</xsd:complexType>

Metadata Processing Warnings and Exceptions

Relationships from a non table per tenant entity to a table per tenant entity will not be allowed. Relationships can only emanate from the table per tenant entity.

NOTE: See future section. We could likely support this using the same cloning strategy we are using with table per tenant descriptors. See the core section below where we discuss the cloning of descriptors. Those entities that refer to table per tenant entities could essentially have their descriptors (and mappings) cloned and modified to refer to the table per tenant descriptor clones.

Property configuration and caching

By default, tenants will share the entity manager factory.

If this is not the desired behavior, the table per tenant property can be set in the persitence unit definition.

  • eclipselink.table-per-tenant

With this property is set, the server session name will be augmented automatically and the session will be isolated to that tenant with full caching capabilities.

Alternatively you may set the existing multitenat properties to disable the shared entity manager factory.

  • eclipselink.multitenant.tenants-share-emf
  • eclipselink.session-name

In a shared entity manager factory approach, a table per tenant context must be provided on each entity manager after a transaction has begun. Setting the table per tenant context must be the first operation on the entity manager and the table per tenant context must not be changed during the lifespan of that entity manager.

By default, tenants will not share the cache, meaning table per tenant entities will have an ISOLATED setting. To allow these entities to share the cache, the following property can be set to true:

eclipselink.multitenant.tenants-share-cache

When the cache is shared, table per tenant entities will have a PROTECTED setting.

Example

<persistence-unit name="multi-tenant-pu">
  ...
  <properties>
    <property name="eclipselink.table-per-tenant-id" value="gpelleti"/>
    ...
  </properties>
</persistence-unit>

Or alternatively (and most likely preferred) in code as follows:

HashMap properties = new HashMap();
properties.put(MULTITENANT_TABLE_PER_TENANT, gpelleti);
...     
EntityManager em = Persistence.createEntityManagerFactory("multi-tenant-pu", properties).createEntityManager();
 
// OR set directly on the Entity Manager.
 
em.setProperty(PersistenceUnitProperties.MULTITENANT_TABLE_PER_TENANT, "gpelleti");

Core

A table per tenant policy will be set per individual entity. This policy is responsible for the initialization of table per tenant descriptors and the table translation name translation per tenant.

Class descriptor will need its clone method expanded to include any missing parts not already included in that method.

Server session:

  • Will contain the metadata processor produced class descriptor.
  • These descriptors are available from the existing descriptor maps and list and will also be paired out to their own list (for ease of manipulation)
  • Unless the table per tenant property is set at the EMF level, table per tenant descriptors will never be initialized. Instead the table per tenant descriptors will cloned for each client session, updated with the table per tenant context property and those cloned descriptor are then initialized and used for the lifespan of that client session.
  • When the table per tenant is provided at the EMF level, the server session descriptors are fully initialized and never cloned per client session. Normal initializing operations will occur in this case. Setting a table per tenant at the EM level here is not allowed and an exception will be thrown.

The meat of a schema table per tenant strategy will be achieved through database level settings and access provisions. The table per tenant property will correlate directly with EclipseLink's descriptor table qualifier (which is added to all tables relating to that descriptor, table and secondary table).

We will also expand this table qualifier setting to all mappings of the descriptor that use a relation table (Many to many, one to one etc.). When those mappings are created, there is no notion of a table qualifier/schema at that point. Since the table per tenant entity is the owner of the relationship it is only fair to assume that its relation table must also have the same qualifier. It is entirely possible though (with unique id's across all tenants for table per tenant entities) to not have this restriction/assumption in place however. See future section.

Under the same assumption, element collection tables should also have the table per tenant identifier as well, but again not necessary when unique id's are employed across all tenants.

In the case of a prefix or suffix solution, instead of setting a table qualifier as described above, all table names from the cloned descriptor and its mappings will need to be updated to the new table name employing the table per tenant context. This name translation will be done by the new table per tenant policy.

Core/Runtime Exceptions

An exception will be raised,

  • when setting a table per tenant at the EM level when one has already been defined at the EMF level.
  • when a table per tenant is not set (database exception in tis case, i.e. invalid table name etc.)
  • when a table per tenant context is set on a non active persistence context (i.e. outside of a transaction)

Properties

The table per tenant property (eclipselink.table-per-tenant-id) will be available from:

  • org.eclipse.persistence.config.EntityManagerProperties.MULTITENANT_TABLE_PER_TENANT
  • org.eclipse.persistence.config.PersistenceUnitProperties.MULTITENANT_TABLE_PER_TENANT

Java example

EntityManager em = createEntityManager(MULTI_TENANT_PU);
em.setProperty("eclipselink.table-per-tenant-id", "gpelleti");
em.setProperty(EntityManagerProperties.MULTITENANT_TABLE_PER_TENANT, "gpelleti");

Querying

Full querying should be available to the user through the following entity manager operations:

  • persist
  • find
  • refresh

And the following queries:

  • named queries
  • update all
  • delete all

NOTE: EclipseLink will not modify, therefore, support multi-tenancy through named native queries. When using these types of queries within a multi-tenant environment, the user will need to be aware and handle any multi-tenancy issues (schemas) themselves directly in their native query. To all intent and purpose, named native queries should be avoided in a multi-tenant environment.

DDL generation

DDL generation will not be supported. Since we a table for all tenants plus the access provisions, there is no way for use to know this information before hand.

Open/Future items

  • A flag or setting to indicate per mapping if its associated table is isolated per tenant or not. This opens up caching issues though, in that, we could never use the level 2 cache and would always have to force the table per tenant entities to an isolated session if the ids are not unique across tenants?? Some unknowns here.
  • Allow for relationships from non table per tenant entities to table per tenant entities. Would need to do more descriptor cloning per entity manager.

Back to the top