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/Examples/JPA/Multitenant"

Line 1: Line 1:
 
EclipseLink (as of 2.3) supports shared multitenant tables using tenant discriminator column(s), allowing an application to be re-used for multiple customers (tenants) and have all their data co-located. All tenants share the same schema without being aware of one another.
 
EclipseLink (as of 2.3) supports shared multitenant tables using tenant discriminator column(s), allowing an application to be re-used for multiple customers (tenants) and have all their data co-located. All tenants share the same schema without being aware of one another.
  
This new functionality is flexible enough to allow for its usage at an Entity Manager Factory level or with individual Entity Manager's based on your application needs. See the notes captured at the bottom of this example.
+
This new functionality is flexible enough to allow for its usage at an Entity Manager Factory level or with individual Entity Manager's based on your application needs. See the examples and notes captured at the bottom of this example which highlights some of the caching differences.
  
 
Support for multitenant entities is done though the usage of the <code>@Multitenant</code> annotation or <code><multitenant></code> xml element. The <code>@Multitenant</code> annotation can be used on an <code>@Entity</code> or <code>@MappedSuperclass</code> and is used in conjunction with the <code>@TenantDiscriminatorColumn</code> or <code><tenant-discriminator-column></code> xml element.
 
Support for multitenant entities is done though the usage of the <code>@Multitenant</code> annotation or <code><multitenant></code> xml element. The <code>@Multitenant</code> annotation can be used on an <code>@Entity</code> or <code>@MappedSuperclass</code> and is used in conjunction with the <code>@TenantDiscriminatorColumn</code> or <code><tenant-discriminator-column></code> xml element.

Revision as of 15:10, 17 June 2011

EclipseLink (as of 2.3) supports shared multitenant tables using tenant discriminator column(s), allowing an application to be re-used for multiple customers (tenants) and have all their data co-located. All tenants share the same schema without being aware of one another.

This new functionality is flexible enough to allow for its usage at an Entity Manager Factory level or with individual Entity Manager's based on your application needs. See the examples and notes captured at the bottom of this example which highlights some of the caching differences.

Support for multitenant entities is done though the usage of the @Multitenant annotation or <multitenant> xml element. The @Multitenant annotation can be used on an @Entity or @MappedSuperclass and is used in conjunction with the @TenantDiscriminatorColumn or <tenant-discriminator-column> xml element.

The tenant discriminator column defines the tenant identifying database column and there may be 1 or more such columns. These columns can be unmapped or mapped. When mapped, they must be marked as read-only. See the annotation and xml examples to follow.

When a multitenant entity is specified, the tenant discriminator column can default. Its default values are:

  • name = TENANT_ID (the database column name)
  • context property = tenant.id (the context property used to populate the database column)

Note, through annotations, specifying only a tenant discriminator column itself does not enable a multitenant entity. At a minimum, the @Multitenant must also be specified. Meaning the minimal configuration is:

@Entity
@Table(name="EMP")
@Multitenant
public Employee() {
  ...
}

The following examples outline other possible configurations using annotations and XML.

/** Single discriminator tenant column **/
 
@Entity
@Table(name = "CUSTOMER")
@Multitenant
@TenantDiscriminatorColumn(name = "TENANT", contextProperty = "multi-tenant.id")
public Customer() {
  ...
}
 
/** 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() {
  ...
}
 
/** 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() {
  ...
}
 
/** 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;
}
<!-- Single tenant discriminator column -->
 
<entity class="model.Customer">
  <multitenant>
    <tenant-discriminator-column name="TENANT context-property="multi-tenant.id""/>
  </multitenant>
  <table name="CUSTOMER"/>
  ...
</entity>
 
<!-- 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>
 
<!-- 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>
 
<!-- 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>

At runtime, the context property configuration can be specified via a persistence unit definition, passed to a create entity manager factory call or set on individual entity managers.

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

Or alternatively in code as follows:

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

An entity Manager property definition would be as follows:

EntityManager em = Persistence.createEntityManagerFactory("multi-tenant-pu").createEntityManager();
em.beginTransaction();
em.setProperty("other.tenant.id.property", "707");
em.setProperty(EntityManagerProperties.MULTITENANT_PROPERTY_DEFAULT, "707");
...

Notes:

  • When a shared persistence unit used, no L2 cache 'striping' is performed, and the eclipselink.multitenant.tenants-share-cache (PersistenceUnitProperties.MULTITENANT_SHARED_EMF) property must be set indicating that all multitenant entities will have a PROTECTED cache setting.
  • When using a non-shared persistence unit, the eclipselink.session-name (PersistenceUnitProperties.SESSION_NAME) property must be provided to ensure a unique server session (and cache) is provided for each tenant.
  • When using Native SQL queries, it is the application's responsibility to ensure that that the tenant discriminator is included in the query.

Back to the top