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/UserGuide/sandbox/gelernter/Multi-Tenant Shared Schema"

m
m
Line 5: Line 5:
 
|example=
 
|example=
 
*[[EclipseLink/Examples/JPA/Multitenant|Multitenant]]
 
*[[EclipseLink/Examples/JPA/Multitenant|Multitenant]]
}}
+
}}  
 +
 
 
= Single-Table Multi-Tenancy  =
 
= Single-Table Multi-Tenancy  =
  
Line 16: Line 17:
 
{{EclipseLink_Note
 
{{EclipseLink_Note
 
|note= In the context of single-table multi-tenancy, “single-table” means multiple tenants can share a single table, and each tenant’s data is distinguished from other tenants’ data via the discriminator column(s). This is opposed to a different kind of multi-tenancy where tenants do not share the same table. It is possible to use multiple tables with single-table multi-tenancy; but in that case, an entity’s persisted data is stored in multiple tables, and multiple tenants can share all the tables.  
 
|note= In the context of single-table multi-tenancy, “single-table” means multiple tenants can share a single table, and each tenant’s data is distinguished from other tenants’ data via the discriminator column(s). This is opposed to a different kind of multi-tenancy where tenants do not share the same table. It is possible to use multiple tables with single-table multi-tenancy; but in that case, an entity’s persisted data is stored in multiple tables, and multiple tenants can share all the tables.  
}}
+
}}  
  
'''''REVIEWERS: I added the above note to head off possible confusion about the terminology. Did I get it right?'''''
+
'''''REVIEWERS: I added the above note to head off possible confusion about the terminology. Did I get it right?'''''  
  
 
{{EclipseLink_AttributeTable
 
{{EclipseLink_AttributeTable
Line 29: Line 30:
 
<td>No</td>
 
<td>No</td>
 
</tr>
 
</tr>
}}
+
}}  
  
 
{{EclipseLink_AttributeTable
 
{{EclipseLink_AttributeTable
Line 76: Line 77:
 
<td>No</td>
 
<td>No</td>
 
</tr>
 
</tr>
}}
+
}}  
 
+
  
 +
<br>
  
 
== Configuring Single-Table Multi-Tenancy  ==
 
== Configuring Single-Table Multi-Tenancy  ==
Line 85: Line 86:
  
 
<ol>
 
<ol>
 
 
<li>Annotate the entity or mapped superclass to use single-table multi-tenancy, using the <tt>@Multitenant</tt> annotation, for example:  
 
<li>Annotate the entity or mapped superclass to use single-table multi-tenancy, using the <tt>@Multitenant</tt> annotation, for example:  
  
Line 104: Line 104:
 
@Multitenant(SINGLE_TABLE)  
 
@Multitenant(SINGLE_TABLE)  
 
@TenantDiscriminatorColumn(name = “TENANT_ID”)
 
@TenantDiscriminatorColumn(name = “TENANT_ID”)
</source>
+
</source>
</li>
+
</ol>
+
  
 
You can specify multiple discriminator columns by using the <tt>@TableDiscriminatorColumns</tt> annotation, for example:  
 
You can specify multiple discriminator columns by using the <tt>@TableDiscriminatorColumns</tt> annotation, for example:  
Line 117: Line 115:
 
     @TenantDiscriminatorColumn(name = "TENANT_ID")
 
     @TenantDiscriminatorColumn(name = "TENANT_ID")
 
     @TenantDiscriminatorColumn(name = "TENANT_CODE")})
 
     @TenantDiscriminatorColumn(name = "TENANT_CODE")})
</source>
+
</source>  
 
+
</li>
 +
<br>
  
 
== Using Discriminator Columns  ==
 
== Using Discriminator Columns  ==
Line 124: Line 123:
 
The following characteristics apply to discriminator columns: &nbsp;  
 
The following characteristics apply to discriminator columns: &nbsp;  
  
* Tenant discriminator columns are completely application definable. '''''REVIEWERS: I’m not sure what this sentence is after. Can I rewrite as “Discriminator columns must be defined by the application.”? '''''
+
*Tenant discriminator columns are completely application definable. '''''REVIEWERS: I’m not sure what this sentence is after. Can I rewrite as “Discriminator columns must be defined by the application.”?'''''
  
* There is no limit on how many tenant discriminator columns an application can define.  
+
*There is no limit on how many tenant discriminator columns an application can define.  
* Any name can be used for a discriminator column.  
+
*Any name can be used for a discriminator column.  
* Tenant discriminator column(s) must always be used with <tt>@Multitenant(SINGLE_TABLE)</tt>. You cannot specify the tenant discriminator column(s) only.  
+
*Tenant discriminator column(s) must always be used with <tt>@Multitenant(SINGLE_TABLE)</tt>. You cannot specify the tenant discriminator column(s) only.  
* Generated schemas can include specified tenant discriminator columns.  
+
*Generated schemas can include specified tenant discriminator columns.  
* Tenant discriminator columns can be mapped or unmapped:
+
*Tenant discriminator columns can be mapped or unmapped:  
** When a tenant discriminator column is mapped, its associated mapping attribute must be marked as read only. With this restriction in place, a tenant discriminator column cannot be part of the entity identifier; it can only be part of the primary key specification on the database.  
+
**When a tenant discriminator column is mapped, its associated mapping attribute must be marked as read only. With this restriction in place, a tenant discriminator column cannot be part of the entity identifier; it can only be part of the primary key specification on the database.  
** On persist, the value of a mapped tenant discriminator column mapping is populated from its associated context property.  
+
**On persist, the value of a mapped tenant discriminator column mapping is populated from its associated context property.  
** Both mapped and unmapped properties are used to form the additional criteria when issuing a SELECT query.  
+
**Both mapped and unmapped properties are used to form the additional criteria when issuing a SELECT query.  
** When a tenant discriminator column is not mapped, the row is populated with the tenant discriminator column’s associated context property value.  
+
**When a tenant discriminator column is not mapped, the row is populated with the tenant discriminator column’s associated context property value.
  
'''''REVIEWERS: I have a question about this item from the design doc: “Unmapped tenant discriminator columns will require EclipseLink to populate the row with the tenant discriminator column’s associated context property value. See Core section below.” '''''
+
'''''REVIEWERS: I have a question about the following item from the design doc: “Unmapped tenant discriminator columns will require EclipseLink to populate the row with the tenant discriminator column’s associated context property value. See Core section below.”'''''  
  
'''''I am confused by this. There is also the item: '''''
+
'''''I am confused by this. There is also the item:'''''  
  
'''''“On persist, the value of a mapped tenant discriminator column mapping is populated from its associated context property. “ It sounds like these two might be saying the same thing for mapped and unmapped. What am I missing? '''''
+
'''''“On persist, the value of a mapped tenant discriminator column mapping is populated from its associated context property. “ It sounds like these two might be saying the same thing for mapped and unmapped. What am I missing?'''''  
  
 
== Using Single-Table Multi-Tenancy in an Inheritence Hierarchy  ==
 
== Using Single-Table Multi-Tenancy in an Inheritence Hierarchy  ==
Line 146: Line 145:
 
Inheritance strategies are configured by specifying the inheritance type (see <tt>@inheritence</tt> in <tt>javax.persistence</tt>). Single-table multi-tenancy can be used in an inheritance hierarchy, as follows:  
 
Inheritance strategies are configured by specifying the inheritance type (see <tt>@inheritence</tt> in <tt>javax.persistence</tt>). Single-table multi-tenancy can be used in an inheritance hierarchy, as follows:  
  
* Multi-tenant metadata can only be applied at the root level of the inheritance hierarchy when using a <tt>SINGLE_TABLE</tt> or <tt>JOINED</tt> inheritance strategy.  
+
*Multi-tenant metadata can only be applied at the root level of the inheritance hierarchy when using a <tt>SINGLE_TABLE</tt> or <tt>JOINED</tt> inheritance strategy.
  
 
*It is possible to specify multi-tenant metadata within a <tt>TABLE_PER_CLASS</tt> inheritance hierarchy.
 
*It is possible to specify multi-tenant metadata within a <tt>TABLE_PER_CLASS</tt> inheritance hierarchy.
  
'''''REVIEWERS: How? Is it that the metadata can be applied somewhere down the hierarchy? '''''
+
'''''REVIEWERS: How? Is it that the metadata can be applied somewhere down the hierarchy?'''''  
  
 
== Annotation Examples  ==
 
== Annotation Examples  ==
Line 163: Line 162:
 
   ...
 
   ...
 
}  
 
}  
</source>
+
</source>  
  
 
The following example defines multiple tenant discriminator columns using multiple tables:  
 
The following example defines multiple tenant discriminator columns using multiple tables:  
Line 187: Line 186:
  
 
}
 
}
</source>
+
</source>  
  
The following example defines a tenant discriminator column mapped as part of the primary key on the database:
+
The following example defines a tenant discriminator column mapped as part of the primary key on the database:  
  
 
<source lang="java">
 
<source lang="java">
Line 199: Line 198:
 
   ...
 
   ...
 
}  
 
}  
</source>
+
</source>  
  
 
The following example defines a mapped tenant discriminator column:  
 
The following example defines a mapped tenant discriminator column:  
Line 211: Line 210:
 
@Basic  
 
@Basic  
 
@Column(name="AGE", insertable="false", updatable="false") public int age; }  
 
@Column(name="AGE", insertable="false", updatable="false") public int age; }  
</source>
+
</source>  
  
 
== Specifying Metadata Using XML  ==
 
== Specifying Metadata Using XML  ==
  
 
You can also use XML to configure single-table multi-tenancy, as an alternative to or in addition to using annotations. Use the following elements in the eclipselink-orm.xml file:  
 
You can also use XML to configure single-table multi-tenancy, as an alternative to or in addition to using annotations. Use the following elements in the eclipselink-orm.xml file:  
* <tt><multitenant> </tt>
 
* <tt>&lt;multitenant-type&gt; </tt>
 
* <tt>&lt;tenant-discriminator-column&gt; </tt>
 
  
For the EclipseLink schemas, see http://wiki.eclipse.org/EclipseLink/XSDs. '''''REVIEWERS: The design doc makes it look like you don’t have to specify &lt;multitenant-type&gt; when using XML. Is that correct? '''''
+
*<tt>&lt;multitenant&gt; </tt>
 +
*<tt>&lt;multitenant-type&gt; </tt>
 +
*<tt>&lt;tenant-discriminator-column&gt; </tt>
  
=== XML Examples ===
+
For the EclipseLink schemas, see http://wiki.eclipse.org/EclipseLink/XSDs. '''''REVIEWERS: The design doc makes it look like you don’t have to specify &lt;multitenant-type&gt; when using XML. Is that correct?'''''
 +
 
 +
=== XML Examples ===
  
 
The following example defines a single tenant discriminator column:  
 
The following example defines a single tenant discriminator column:  
Line 234: Line 234:
 
   ...
 
   ...
 
</entity>  
 
</entity>  
</source>
+
</source>  
  
 
The following example defines multiple tenant discriminator columns using multiple tables:  
 
The following example defines multiple tenant discriminator columns using multiple tables:  
Line 249: Line 249:
 
   ...
 
   ...
 
</entity>   
 
</entity>   
</source>
+
</source>  
  
 
The following example defines a tenant discriminator column mapped as part of the primary key on the database:  
 
The following example defines a tenant discriminator column mapped as part of the primary key on the database:  
Line 261: Line 261:
 
   ...
 
   ...
 
</entity>  
 
</entity>  
</source>
+
</source>  
  
The following example defines a mapped tenant discriminator column:
+
The following example defines a mapped tenant discriminator column:  
  
 
<source lang="xml">
 
<source lang="xml">
Line 280: Line 280:
 
   ...
 
   ...
 
</entity>   
 
</entity>   
</source>
+
</source>  
  
 
== Property Configuration and Caching Scope  ==
 
== Property Configuration and Caching Scope  ==
Line 286: Line 286:
 
At runtime, context properties can be specified in a persistence unit definition or passed to a <tt>CreateEntityManagerFactory()</tt> call, as shown in the following examples.  
 
At runtime, context properties can be specified in a persistence unit definition or passed to a <tt>CreateEntityManagerFactory()</tt> call, as shown in the following examples.  
  
The order of precedence for tenant discriminator column properties is as follows, listed from highest priority to lowest: ''''''&lt;-- REVIEWERS: is that right? Highest to lowest? '''''
+
The order of precedence for tenant discriminator column properties is as follows, listed from highest priority to lowest: ''''''&lt;-- REVIEWERS: is that right? Highest to lowest?'''''  
  
# <tt>EntityManager </tt>
+
#<tt>EntityManager </tt>  
# <tt>EntityManagerFactory </tt>
+
#<tt>EntityManagerFactory </tt>  
# Application context (when in a Java EE container)
+
#Application context (when in a Java EE container)
  
 
The following example shows a property set in a persistence unit definition in the <tt>persistence.xml</tt> file:  
 
The following example shows a property set in a persistence unit definition in the <tt>persistence.xml</tt> file:  
Line 302: Line 302:
 
   </properties>
 
   </properties>
 
</persistence-unit>  
 
</persistence-unit>  
</source>
+
</source>  
  
 
The following example shows the property set in code:  
 
The following example shows the property set in code:  
Line 312: Line 312:
 
EntityManager em = Persistence.createEntityManagerFactory("multi-tenant",  
 
EntityManager em = Persistence.createEntityManagerFactory("multi-tenant",  
 
   properties).createEntityManager();  
 
   properties).createEntityManager();  
</source>
+
</source>  
  
 
=== Entity Manager Factory  ===
 
=== Entity Manager Factory  ===
Line 325: Line 325:
 
EntityManager em = Persistence.createEntityManagerFactory("multi-tenant",  
 
EntityManager em = Persistence.createEntityManagerFactory("multi-tenant",  
 
   properties).createEntityManager();  
 
   properties).createEntityManager();  
</source>
+
</source>  
  
 
=== Shared Entity Manager Factory  ===
 
=== Shared Entity Manager Factory  ===
Line 343: Line 343:
 
EntityManager em = Persistence.createEntityManagerFactory("multi-tenant",cacheProperties).createEntityManager(tenantProperties);
 
EntityManager em = Persistence.createEntityManagerFactory("multi-tenant",cacheProperties).createEntityManager(tenantProperties);
 
...  
 
...  
</source>
+
</source>  
  
 
Swapping tenant ID during an active <tt>EntityManager</tt> is not allowed.  
 
Swapping tenant ID during an active <tt>EntityManager</tt> is not allowed.  
Line 358: Line 358:
 
You can specify default tenant discriminator column metadata using the &lt;persistence-unit-defaults&gt; element and its &lt;tenant-discriminator-column&gt; subelement. When defined at this level, it applies to all entities of the persistence unit that specify the SINGLE_TABLE multi-tenant type, excluding those that specify their own tenant discriminator metadata. For example:  
 
You can specify default tenant discriminator column metadata using the &lt;persistence-unit-defaults&gt; element and its &lt;tenant-discriminator-column&gt; subelement. When defined at this level, it applies to all entities of the persistence unit that specify the SINGLE_TABLE multi-tenant type, excluding those that specify their own tenant discriminator metadata. For example:  
  
'''''REVIEWERS: I came up with this example myself. Is it correct? If not, please advise. '''''
+
'''''REVIEWERS: I came up with this example myself. Is it correct? If not, please advise.'''''  
  
 
<source lang="xml">
 
<source lang="xml">
Line 368: Line 368:
 
   </persistence-unit-defaults>
 
   </persistence-unit-defaults>
 
</persistence-unit-metadata>
 
</persistence-unit-metadata>
</source>
+
</source>  
  
 
Note: With no defaults, an entity not marked with multi-tenant metadata will not use any multi-tenancy strategy.  
 
Note: With no defaults, an entity not marked with multi-tenant metadata will not use any multi-tenancy strategy.  
Line 376: Line 376:
 
You can also specify tenant discriminator column metadata using &lt;entity-mappings&gt; element. This overrides persistence unit defaults (described above) and applies to all entities of the given mapping file that specify the SINGLE_TABLE multi-tenant type, excluding those entities that specify their own tenant discriminator metadata. For example,  
 
You can also specify tenant discriminator column metadata using &lt;entity-mappings&gt; element. This overrides persistence unit defaults (described above) and applies to all entities of the given mapping file that specify the SINGLE_TABLE multi-tenant type, excluding those entities that specify their own tenant discriminator metadata. For example,  
  
''''''REVIEWERS: I came up with this example myself. Is it correct? If not, please advise. ''''''
+
''''''REVIEWERS: I came up with this example myself. Is it correct? If not, please advise. ''''''  
  
 
<source lang="xml">
 
<source lang="xml">
Line 383: Line 383:
 
     <tenant-discriminator-column name="TENANT” context-property="multi-tenant.id"/>
 
     <tenant-discriminator-column name="TENANT” context-property="multi-tenant.id"/>
 
</entity-mappings>  
 
</entity-mappings>  
</source>
+
</source>  
  
 
=== Example  ===
 
=== Example  ===
Line 400: Line 400:
 
   ...
 
   ...
 
} &nbsp;  
 
} &nbsp;  
</source>
+
</source>  
  
 
=== Public Default Context Property  ===
 
=== Public Default Context Property  ===
Line 415: Line 415:
 
em.setProperty("tenant.id", "707");  
 
em.setProperty("tenant.id", "707");  
 
em.setProperty(EntityManagerProperties.MULTITENANT_PROPERTY_DEFAULT, "707");  
 
em.setProperty(EntityManagerProperties.MULTITENANT_PROPERTY_DEFAULT, "707");  
</source>
+
</source>  
  
 
Support for Tenant Discriminator Columns through Entity Manager Operations and Querying The tenant discriminator column and value are supported through the following entity manager operations:  
 
Support for Tenant Discriminator Columns through Entity Manager Operations and Querying The tenant discriminator column and value are supported through the following entity manager operations:  
  
*<tt>persist() </tt>
+
*<tt>persist() </tt>  
*<tt>find() </tt>
+
*<tt>find() </tt>  
 
*<tt>refresh()</tt>
 
*<tt>refresh()</tt>
  
Line 429: Line 429:
 
*Delete all
 
*Delete all
  
'''''REVIEWERS: Do you think any examples would be appropriate here? If so, please provide. '''''
+
'''''REVIEWERS: Do you think any examples would be appropriate here? If so, please provide.'''''  
  
 
{{EclipseLink_Note
 
{{EclipseLink_Note
 
|note=EclipseLink does not support multi-tenancy through named native queries. If you want to use named native queries in a multi-tenant environment, you must handle any multi-tenancy issues directly in the query. In general, it is best to avoid named native queries in a multi-tenant environment.
 
|note=EclipseLink does not support multi-tenancy through named native queries. If you want to use named native queries in a multi-tenant environment, you must handle any multi-tenancy issues directly in the query. In general, it is best to avoid named native queries in a multi-tenant environment.
 
}}
 
}}

Revision as of 19:27, 16 June 2011


Eclipselink-logo.gif
EclipseLink
Website
Download
Community
Mailing ListForumsIRCmattermost
Issues
OpenHelp WantedBug Day
Contribute
Browse Source

Elug example icon.png Examples


Single-Table Multi-Tenancy

The SINGLE_TABLE multi-tenant type specifies that any table to which an entity or mapped superclass maps can include rows for multiple tenants. Access to tenant-specific rows is restricted to the tenant.

Tenant-specific rows are associated with the tenant by using tenant discriminator columns. The discriminator columns are used with application context values to limit what a persistence context can access.

The results of queries on the mapped tables are limited to the tenant discriminator value(s) provided as property values. This applies to all insert, update, and delete operations on the table. When multi-tenant metadata is applied at the mapped superclass level, it is applied to all subentities unless they specify their own multi-tenant metadata. REVIEWERS: I didn’t include any of the info re: homogeneity from the design doc, because I took it to be implementation details that will be pertinent when TABLE_PER_TENANT is available. Therefore, I left it out. Please advise if I should add any back in.

Elug note icon.png

Note: In the context of single-table multi-tenancy, “single-table” means multiple tenants can share a single table, and each tenant’s data is distinguished from other tenants’ data via the discriminator column(s). This is opposed to a different kind of multi-tenancy where tenants do not share the same table. It is possible to use multiple tables with single-table multi-tenancy; but in that case, an entity’s persisted data is stored in multiple tables, and multiple tenants can share all the tables.

REVIEWERS: I added the above note to head off possible confusion about the terminology. Did I get it right?

@Multitenant Attributes
Attribute Description Default Required?
MultitenantType Specifies the multi-tenant strategy to use (SINGLE_TABLE).   No
@TenantDiscriminatorColumn Attributes
Attribute Description Default Required?
columnDefinition The SQL fragment that is used when generating the DDL for the discriminator column. The provider-generated SQL to create a column of the specified discriminator type. No
contextProperty The name of the context property to apply to the tenant discriminator column. eclipselink.tenant-id No
discriminatorType The type of object/column to use as a class discriminator. javax.persistence.DiscriminatorType.STRING No
length The column length for String-based discriminator types. The column length for String-based discriminator types. Ignored for other discriminator types. No
name The name of column to be used for the tenant discriminator. TENANT_ID No
primaryKey Specifies that the tenant discriminator column is part of the primary key of the tables. Specifies that the tenant discriminator column is part of the primary key of the tables. Yes
table The name of the table that contains the column. The name of the table that contains the column. If absent the column is assumed to be in the primary table. This attribute must be specified if the column is on a secondary table. No


Configuring Single-Table Multi-Tenancy

To configure single-table multi-tenancy, you must specify both of the following:

  1. Annotate the entity or mapped superclass to use single-table multi-tenancy, using the @Multitenant annotation, for example:
    @Entity
    @Table(name=“EMP”)
    @Multitenant(SINGLE_TABLE)

    SINGLE_TABLE states that the table or tables (Table and SecondaryTable) associated with the given entity can be shared among tenants.

  2. Specify the column or columns to be used as the discriminator column, using the @TenantDiscriminatorColumn annotation, for example:
    @Entity 
    @Table(name=“EMP”) 
    @Multitenant(SINGLE_TABLE) 
    @TenantDiscriminatorColumn(name = “TENANT_ID”)

    You can specify multiple discriminator columns by using the @TableDiscriminatorColumns annotation, for example:

    @Entity 
    @Table(name = "EMPLOYEE") 
    @Multitenant(SINGLE_TABLE) 
    @TenantDiscriminatorColumns({ 
        @TenantDiscriminatorColumn(name = "TENANT_ID")
        @TenantDiscriminatorColumn(name = "TENANT_CODE")})

  3. Using Discriminator Columns

    The following characteristics apply to discriminator columns:  

    • Tenant discriminator columns are completely application definable. REVIEWERS: I’m not sure what this sentence is after. Can I rewrite as “Discriminator columns must be defined by the application.”?
    • There is no limit on how many tenant discriminator columns an application can define.
    • Any name can be used for a discriminator column.
    • Tenant discriminator column(s) must always be used with @Multitenant(SINGLE_TABLE). You cannot specify the tenant discriminator column(s) only.
    • Generated schemas can include specified tenant discriminator columns.
    • Tenant discriminator columns can be mapped or unmapped:
      • When a tenant discriminator column is mapped, its associated mapping attribute must be marked as read only. With this restriction in place, a tenant discriminator column cannot be part of the entity identifier; it can only be part of the primary key specification on the database.
      • On persist, the value of a mapped tenant discriminator column mapping is populated from its associated context property.
      • Both mapped and unmapped properties are used to form the additional criteria when issuing a SELECT query.
      • When a tenant discriminator column is not mapped, the row is populated with the tenant discriminator column’s associated context property value.

    REVIEWERS: I have a question about the following item from the design doc: “Unmapped tenant discriminator columns will require EclipseLink to populate the row with the tenant discriminator column’s associated context property value. See Core section below.”

    I am confused by this. There is also the item:

    “On persist, the value of a mapped tenant discriminator column mapping is populated from its associated context property. “ It sounds like these two might be saying the same thing for mapped and unmapped. What am I missing?

    Using Single-Table Multi-Tenancy in an Inheritence Hierarchy

    Inheritance strategies are configured by specifying the inheritance type (see @inheritence in javax.persistence). Single-table multi-tenancy can be used in an inheritance hierarchy, as follows:

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

    REVIEWERS: How? Is it that the metadata can be applied somewhere down the hierarchy?

    Annotation Examples

    The following example defines a single discriminator tenant column:

    @Entity 
    @Table(name = "CUSTOMER") 
    @Multitenant @TenantDiscriminatorColumn(name = "TENANT", contextProperty = "multi-tenant.id")
    public Customer() { 
      ...
    }

    The following example defines multiple tenant discriminator columns using multiple tables:

    @Entity 
    @Table(name = "EMPLOYEE") 
    @SecondaryTable(name = "RESPONSIBILITIES") 
    @Multitenant(SINGLE_TABLE) 
    @TenantDiscriminatorColumns({ 
        @TenantDiscriminatorColumn(name = "TENANT_ID", 
            contextProperty = "employee-tenant.id", 
            length = 20)
        @TenantDiscriminatorColumn(name = "TENANT_CODE", 
            contextProperty = "employee-tenant.code", 
            discriminatorType = STRING, 
            table = "RESPONSIBILITIES")
      }
     
    ) public Employee() { 
     
      ...
     
    }

    The following example defines a tenant discriminator column mapped as part of the primary key on the database:

    @Entity 
    @Table(name = "ADDRESS") 
    @Multitenant 
    @TenantDiscriminatorColumn(name = "TENANT", contextProperty = "tenant.id", primaryKey = true)
    public Address() { 
      ...
    }

    The following example defines a mapped tenant discriminator column:

    @Entity @Table(name = "Player") 
    @Multitenant 
    @TenantDiscriminatorColumn(name = "AGE", contextProperty = "tenant.age")
    public Player() { 
      ...
    @Basic 
    @Column(name="AGE", insertable="false", updatable="false") public int age; }

    Specifying Metadata Using XML

    You can also use XML to configure single-table multi-tenancy, as an alternative to or in addition to using annotations. Use the following elements in the eclipselink-orm.xml file:

    • <multitenant>
    • <multitenant-type>
    • <tenant-discriminator-column>

    For the EclipseLink schemas, see http://wiki.eclipse.org/EclipseLink/XSDs. REVIEWERS: The design doc makes it look like you don’t have to specify <multitenant-type> when using XML. Is that correct?

    XML Examples

    The following example defines a single tenant discriminator column:

    <entity class="model.Customer"> 
      <multitenant>
        <tenant-discriminator-column name="TENANT” context-property="multi-tenant.id"/> 
      </multitenant>
      <table name="CUSTOMER"/>
      ...
    </entity>

    The following example defines multiple tenant discriminator columns using multiple tables:

    <entity class="model.Employee"> 
      <multitenant type="SINGLE_TABLE">
        <tenant-discriminator-column name="TENANT_ID" context-property="employee-tenant.id" length="20"/>
        <tenant-discriminator-column name="TENANT_CODE" context-property="employee-tenant.id" 
           discriminator-type="STRING" table="RESPONSIBILITIES"/> 
      </multitenant>
      <table name="EMPLOYEE"/>
      <secondary-table name="RESPONSIBILITIES"/>
      ...
    </entity>

    The following example defines a tenant discriminator column mapped as part of the primary key on the database:

    <entity class="model.Address"> 
      <multitenant>
        <tenant-discriminator-column name="TENANT" context-property="multi-tenant.id" primary-key="true"/>
      </multitenant>
      <table name="ADDRESS"/>
      ...
    </entity>

    The following example defines a mapped tenant discriminator column:

     <entity class="model.Player"> 
      <multi-tenant>
        <tenant-discriminator-column name="AGE" context-property="tenant.age"/>
      </multi-tenant>
      <table name="PLAYER"/>
      ...
      <attributes>
        <basic name="age" insertable="false" updatable="false">
          <column name="AGE"/>
        </basic>
        ...
      </attributes>
      ...
    </entity>

    Property Configuration and Caching Scope

    At runtime, context properties can be specified in a persistence unit definition or passed to a CreateEntityManagerFactory() call, as shown in the following examples.

    The order of precedence for tenant discriminator column properties is as follows, listed from highest priority to lowest: '<-- REVIEWERS: is that right? Highest to lowest?

    1. EntityManager
    2. EntityManagerFactory
    3. Application context (when in a Java EE container)

    The following example shows a property set in a persistence unit definition in the persistence.xml file:

    <persistence-unit name="multi-tenant"> 
      ...
      <properties>
        <property name="tenant.id" value="707"/>
        ...
      </properties>
    </persistence-unit>

    The following example shows the property set in code:

    HashMap properties = new HashMap(); 
    properties.put("tenant.id", "707"); 
    ... 
    EntityManager em = Persistence.createEntityManagerFactory("multi-tenant", 
      properties).createEntityManager();

    Entity Manager Factory

    At the level of the entity manager factory, you must provide a unique session name through the eclipselink.session-name property, to ensure a unique server session (and cache) is provided for each tenant. This allows for user-defined properties (without any prefixing). For example:

    HashMap properties = new HashMap(); 
    properties.put("tenant.id", "707"); 
    properties.put("eclipselink.session-name", "multi-tenant-707"); 
    ... 
    EntityManager em = Persistence.createEntityManagerFactory("multi-tenant", 
      properties).createEntityManager();

    Shared Entity Manager Factory

    When using a shared entity manager factory, you must set following property to indicate the factory will be shared: eclipselink.multitenant.tenants-share-cache

    When this property is set, all multitenant entities will have a PROTECTED cache setting. Entity Manager At the level of the entity manager, you must specify caching strategies, because the same server session can be used for each tenant. For example, you can use an isolation level to ensure no shared tenant information exists in the L2 cache. These settings are set when creating the entity manager factory. For example:

    HashMap tenantProperties = new HashMap(); 
    properties.put("tenant.id", "707"); 
    HashMap cacheProperties = new HashMap(); 
    properties.put("eclipselink.cache.shared.Employee", "false"); 
    properties.put("eclipselink.cache.size.Address", "10"); 
    properties.put("eclipselink.cache.type.Contract", "NONE"); 
    ... 
    EntityManager em = Persistence.createEntityManagerFactory("multi-tenant",cacheProperties).createEntityManager(tenantProperties);
    ...

    Swapping tenant ID during an active EntityManager is not allowed.

    Defining Persistence Unit and Entity Mappings Defaults

    You can define single-table multi-tenancy for specific entities and mapped superclasses, as described above. In addition, you can define the metadata at higher levels in the eclipselink-orm.xml file, to provide defaults. Standard JPA metadata defaulting and overriding rules apply. The elements used for specifying these defaults are:

    • <persistence-unit-defaults>
    • <entity-mappings>

    Persistence Unit Defaults

    You can specify default tenant discriminator column metadata using the <persistence-unit-defaults> element and its <tenant-discriminator-column> subelement. When defined at this level, it applies to all entities of the persistence unit that specify the SINGLE_TABLE multi-tenant type, excluding those that specify their own tenant discriminator metadata. For example:

    REVIEWERS: I came up with this example myself. Is it correct? If not, please advise.

    <persistence-unit-metadata> 
      <persistence-unit-defaults>
      ...
        <tenant-discriminator-column name="TENANT” context-property="multi-tenant.id"/> 
        </multitenant>
      </persistence-unit-defaults>
    </persistence-unit-metadata>

    Note: With no defaults, an entity not marked with multi-tenant metadata will not use any multi-tenancy strategy.

    Entity Mappings Defaults

    You can also specify tenant discriminator column metadata using <entity-mappings> element. This overrides persistence unit defaults (described above) and applies to all entities of the given mapping file that specify the SINGLE_TABLE multi-tenant type, excluding those entities that specify their own tenant discriminator metadata. For example,

    'REVIEWERS: I came up with this example myself. Is it correct? If not, please advise. '

    <entity-mappings> 
      ...
        <tenant-discriminator-column name="TENANT” context-property="multi-tenant.id"/>
    </entity-mappings>

    Example

    Defaults always apply even when there are multiple tenant discriminators. This allows you to map several columns for the same property. For example, the code below defaults the property name to eclipselink.tenant-id and states it should be writing the TENANT column for both the EMPLOYEE and SALARY table.

    @Entity @Table(name = "EMPLOYEE") 
    @SecondaryTable(name = "SALARY") 
    @MultiTenant(SINGLE_TABLE) @TenantDiscriminatorColumns({ 
        @TenantDiscriminatorColumn(name = "TENANT")
        @TenantDiscriminatorColumn(name = "TENANT", table = "SALARY")
      }
     
    ) public Employee() { 
      ...
    } &nbsp;

    Public Default Context Property

    The public default context property (eclipselink.tenant-id) definition is available from:

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

    For example:

    EntityManager em = createEntityManager(MULTI_TENANT_PU); 
    em.setProperty("tenant.id", "707"); 
    em.setProperty(EntityManagerProperties.MULTITENANT_PROPERTY_DEFAULT, "707");

    Support for Tenant Discriminator Columns through Entity Manager Operations and Querying The tenant discriminator column and value are supported through the following entity manager operations:

    • persist()
    • find()
    • refresh()

    They are also supported through the following queries:

    • Named queries
    • Update all
    • Delete all

    REVIEWERS: Do you think any examples would be appropriate here? If so, please provide.

    Elug note icon.png

    Note: EclipseLink does not support multi-tenancy through named native queries. If you want to use named native queries in a multi-tenant environment, you must handle any multi-tenancy issues directly in the query. In general, it is best to avoid named native queries in a multi-tenant environment.

Back to the top