Difference between revisions of "EclipseLink/Examples/JPA/Multitenant"

From Eclipsepedia

Jump to: navigation, search
(Property configuration and caching scope)
(Property configuration and caching scope)
Line 131: Line 131:
 
</source>
 
</source>
  
=== Property configuration and caching scope ===
+
=== Context property configuration ===
  
At runtime the properties can be specified via a persistence unit definition or passed to a create entity manager factory call.
+
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.
 
+
The order of precendence for tenant discriminator column properties is as follows:
+
* EntityManager
+
* EntityManagerFactory
+
* Application context (when in a Java EE container)
+
  
 
<source lang="xml">
 
<source lang="xml">
Line 154: Line 149:
 
<source lang="java">
 
<source lang="java">
 
HashMap properties = new HashMap();
 
HashMap properties = new HashMap();
properties.put(EntityManagerProperties.MULTITENANT_PROPERTY_DEFAULT, "707");
+
properties.put(PersistenceUnitProperties.MULTITENANT_PROPERTY_DEFAULT, "707");  
...   
+
EntityManager em = Persistence.createEntityManagerFactory("multi-tenant-pu", properties).createEntityManager();
EntityManager em = Persistence.createEntityManagerFactory("multi-tenant", properties).createEntityManager();
+
 
</source>
 
</source>
  
==== Entity Manager Factory ====
+
At the Entity Manager level.
 
+
At this level, a unique session name must be provided using 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).
+
  
 
<source lang="java">
 
<source lang="java">
HashMap properties = new HashMap();
+
EntityManager em = Persistence.createEntityManagerFactory("multi-tenant-pu").createEntityManager();
properties.put(PersistenceUnitProperties.MULTITENANT_PROPERTY_DEFAULT, "707");
+
em.beginTransaction();
properties.put("eclipselink.session-name", "multi-tenant-707");
+
em.setProperty("tenant.id", "707");
...   
+
em.setProperty(EntityManagerProperties.MULTITENANT_PROPERTY_DEFAULT, "707");
EntityManager em = Persistence.createEntityManagerFactory("multi-tenant", properties).createEntityManager();
+
...
 
</source>
 
</source>
  
===== Shared Entity Manager Factory =====
+
When a shared persistence unit used, no L2 cache 'striping' is performed, and the <code>eclipselink.multitenant.tenants-share-cache</code> property must be set indicating that all multitenant entities will have a PROTECTED cache setting.
  
When using a shared entity manager factory, no L2 cache "striping' will be performed. The following property must be set to indicate the EMF will be shared:
+
When using a non-shared persistence unit, the <code>eclipselink.session-name</code> property must be provided to ensure a unique server session (and cache) is provided for each tenant.
 
+
<source lang="XML">
+
eclipselink.multitenant.tenants-share-cache
+
</source>
+
 
+
When this property is set, all multitenant entities will have a PROTECTED cache setting.
+
 
+
==== Entity Manager ====
+
 
+
At this level, users will be required to specify the caching strategies as the same server session can be employed for each tenant. Users may decide to us an isolation level here etc to ensure no 'shared' tenant information exists in the L2 cache. These settings are set when creating the entity manager factory.
+
 
+
Swapping tenant id during a live EntityManager is not allowed.
+
 
+
<source lang="java">
+
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);
+
...
+
</source>
+

Revision as of 15:21, 6 June 2011

EclipseLink (as of 2.3) supports a shared (striped) multitenant table using a tenant discriminator column(s).

EclipseLink supports multitenant entities using its @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.

Annotation examples

/** 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;
}

XML examples

<!-- 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>

Context property configuration

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();

At the Entity Manager level.

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

When a shared persistence unit used, no L2 cache 'striping' is performed, and the eclipselink.multitenant.tenants-share-cache 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 property must be provided to ensure a unique server session (and cache) is provided for each tenant.