ヘッダーをスキップ
Oracle® Fusion Middleware Oracle TopLinkソリューション・ガイド
12c (12.1.3)
E57549-01
  ドキュメント・ライブラリへ移動
ライブラリ
製品リストへ移動
製品
目次へ移動
目次

前
 
次
 

14 TopLinkを使用したテナントの分離

この章では、Oracle TopLinkでアプリケーションを1つ開発し、それを別の複数のクライアント(つまりテナント)に、アプリケーションの割合、データの分離度およびテナントに固有の機能を変えてデプロイする方法について説明します。たとえば、規模の大きな企業が1つの給与アプリケーションを、複数の部門で使用するよう開発したとします。各部門は、独自のデータと共有データにアクセスできますが、他の部門のデータはまったく参照することができません。

この章の内容は次のとおりです。

ユース・ケース

Saas環境などで、アプリケーションの複数のクライアントで、データ・ソースを共有しながら、自分のデータにはプライベートにアクセスできる必要があります。

解決方法

テナントを分離する戦略を決定し、TopLinkのテナントの分離機能を使用してその戦略を導入します。

コンポーネント

14.1 ソリューションの概要

EclipseLinkでは、テナントを分離する機能の設計および実装をかなり柔軟にできます。次などが可能です。

アプリケーションの分離オプション

データの分離オプション

EclipseLinkには、データ・ソースでマルチテナンシを提供する次のオプションがあります。

EclipseLinkでは、拡張可能なエンティティおよびMetadataSourceを使用した拡張可能なエンティティを介したテナントに固有の拡張も提供しています。それらの機能の詳細は、第12章「JPAエンティティおよびJAXB Beanの拡張可能化」および第13章「外部メタデータ・ソースの使用」を参照してください。

14.2 単一表マルチテナントの使用

単一表マルチテナントでは、エンティティまたはマップ済のスーパークラスがマップされるすべての表(TableまたはSecondaryTable)に、複数のテナント用の行を含めることができます。テナントに固有の行へのアクセスは、指定のテナントに制限されています。

テナント固有の行は、1つ以上のテナント識別子列を使用してテナントに関連付られています。識別子列には、どのような永続性コンテンツがアクセスできるか制限するアプリケーション・コンテキスト値が使用されています。

マップした表に対する問合せの結果は、プロパティ値として提供されたテナントの識別子の値に制限されます。これは、表でのすべてのinsert、updateおよびdelete操作に該当します。マルチテナントのメタデータがマップ済のスーパークラス・レベルで適用されると、独自のマルチテナント・メタデータを指定しないかぎり、すべてのサブエンティティに適用されます。


注意:

単一表マルチテナントのコンテキストで、単一表は、複数のテナントが1つの表を共有し、各テナントのデータは識別子列で他のテナントのデータから区別されていることを意味します。単一表マルチテナントでは、複数の表を使用することも可能ですが、その場合は、エンティティの永続性データは複数の表に保存され、複数のテナントはすべての表を共有できます。


14.2.1 単一表マルチテナントを使用する場合の主なタスク

単一表マルチテナントを使用する場合には、次のテスクを実行します。

14.2.1.1 タスク1: 前提条件

単一表マルチテナントを実装するには、次が必要です。

14.2.1.2 タスク2: 単一表マルチテナントの有効化

単一表マルチテナントは、@Multitenant注釈を使用するか、オブジェクト・リレーショナル・マッピング(ORM) XMLファイルで<multitenant>要素を使用するか、注釈とXMLの両方を使用して、宣言することにより有効にできます。

14.2.1.2.1 @Multitenant注釈の使用

@Multitenant注釈を使用する場合、@Entityまたは@MappedSuperclass注釈に含めます。次に例を示します。

@Entity
@Table(name=”EMP”)
@Multitenant(SINGLE_TABLE)
public class Employee {
}

注意:

単一表はマルチテナントのデフォルトのタイプなので、SINGLE_TABLE@Multitenantに含める必要はありません。



注意:

識別子列はプライマリ表にあることが前提となっているので@Tableの注釈は不要です。ただし、セカンダリ表に識別子列を定義している場合、その表を@SecondaryTableを使用して識別する必要があります。


14.2.1.2.2 <multitenant>要素の使用

<multitenant>要素を使用する場合は、<entity>要素内にこの要素を指定します。次に例を示します。

<entity class="model.Employee">
   <multitenant type="SINGLE_TABLE">
   ...
   </multitenant>
   ...
</entity>

14.2.1.3 タスク3: テナント識別子列の指定

識別子列は、関連付けられているアプリケーション・コンテキストとともに使用して、表のどの行にアプリケーション・テナントがアクセスできるかを示します。

テナント識別子列は、@TenantDiscriminatorColumn注釈を指定するか、オブジェクト・リレーショナル(ORM) XMLファイルで<tenant-discriminator-column>要素を使用して、宣言することにより指定できます。

識別子列には、次の特徴があります。

  • テナントの識別子列を使用する場合、常に@Multitenant (またはORM XMLファイルで<multitenant>)も使用する必要があります。テナント識別子列のみの指定はできません。

  • テナント識別子列は、別の表が明示的に指定されている場合を除きプライマリ表上にあることが前提となっています。

  • 永続性では、テナント識別子列の値はそれに関連付られているコンテンツのプロパティから移入されます。

  • マルチテナント・エンティティが指定されている場合、テナント識別子列はデフォルトのものとなります。デフォルト値は次のとおりです。

    • Name = TENANT_ID (データベースの列名)

    • Context property = eclipselink.tenant.id (データベース列の移入に使用されるコンテキスト・プロパティ)

  • テナント識別子列はアプリケーションで定義できます。つまり、各共有エンティティ表で、識別子列は特定の列には関連付られていません。TENANT_IDT_IDなどを使用できます。

  • アプリケーションで定義できるテナント識別子列の数には制限はありません。

  • 識別子列には、任意の名前を使用できます。

  • 生成されるスキーマには、指定したテナント識別子列などが含まれます。

  • テナント識別子列は、マッピングすることもマッピング解除することもできます。

    • テナント識別子列がマップされたら、関連付られているマッピング属性は読取り専用としてマークする必要があります。

    • マップおよびマップ解除されている両方のプロパティは、SELECT問合せの発行時に追加の基準を形成するために使用します。

14.2.1.3.1 @TenantDiscriminatorColumn注釈の使用

@TenantDiscriminatorColumn注釈を使用する場合、エンティティまたはマップ済のスーパークラスの@Multitenant注釈に追加して、オプションでnameおよびcontextProperty属性を含めます。これらの属性を指定しない場合、デフォルトのname = "TENANT-ID"およびcontextProperty = "eclipselink.tenant-id"が使用されます。

次に例を示します。

@Entity
@Multitenant(SINGLE_TABLE)
@TenantDiscriminatorColumn(name = "TENANT", contextProperty = "multitenant.id")
public class Employee {
}

複数のテナント識別子列を指定するには、@TenantDiscriminatorColumns注釈内に複数の@TenantDiscriminatorColumn注釈を含め、プライマリ表にない場合は、列のある表を含めます。次に例を示します。

@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() {
   ...
}
14.2.1.3.2 <tenant-discriminator-column>要素の使用

<tenant-discriminator-column>要素を使用する場合は、<multitenant>要素内にこの要素を指定して、オプションでnameおよびcontext-property属性を指定します。これらの属性を指定しない場合、デフォルトのname = "TENANT-ID"およびcontextProperty = "eclipselink.tenant-id"が使用されます。

次に例を示します。

<entity class="model.Employee">
   <multitenant>
      <tenant-discriminator-column name="TENANT"
         context-property="multitenant.id"/>
   </multitenant>
   ...
</entity>

複数の列を指定する場合は、追加の<tenant-discriminator-column>要素を指定して、プライマリ表に列がない場合は、列がある表を指定します。次に例を示します。

<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>
14.2.1.3.3 テナント識別子列のマッピング

テナント識別子列は、主キーまたは別の列にマッピングできます。次の例では、DDL生成時に、表の主キーにテナント識別子列をマッピングしています。

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

次の例では、ORM XMLファイルを使用してテナント識別子列を主キーにマップしています。

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

次の例では、テナント識別子列をAGEという別の列にマップしています。

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

次の例では、ORM XMLファイルを使用し、テナント識別子列をAGEという名前の別の列にマップしています。

<entity class="model.Player">
  <multitenant>
    <tenant-discriminator-column name="AGE" context-property="tenant.age"/>
  </multitenant>
  <table name="PLAYER"/>
  ...
  <attributes>
    <basic name="age" insertable="false" updatable="false">
      <column name="AGE"/>
    </basic>
    ...
  </attributes>
  ...
</entity>
14.2.1.3.4 永続性ユニットおよびエンティティ・マッピングのデフォルトの定義

識別子列は、エンティティおよびマップ済スーパークラス・レベルのみでなく、persistence-unit-defaultsおよびentity-mappingsレベルで構成してデフォルトとすることができます。これらのレベルでメタデータを定義する場合、同様なJPAのメタデータのデフォルト設定およびオーバーラード・ルールに従います。

デフォルトのテナント識別子列メタデータは、ORM XMLファイルのpersistence-unit-defaultsレベルで指定します。このレベルで定義すると、SINGLE_TABLEのマルチテナント・タイプが指定された永続性ユニットのすべてのエンティティにデフォルトが適用されます(独自のテナント識別子メタデータが指定されているものは除きます)。次に例を示します。

<persistence-unit-metadata>
  <persistence-unit-defaults>
    <tenant-discriminator-column name="TENANT_ID" context-property="tenant.id"/>
  </persistence-unit-defaults>
</persistence-unit-metadata>

テナント識別子列メタデータは、ORM XMLファイルのentity-mappingsレベルで指定することも可能です。このレベルで定義すると、永続性ユニットのデフォルトがオーバーライドされ、マッピング・ファイルでマルチテナント・タイプにSINGLE_TABLEと指定されたすべてのエンティティにそれが適用されます(独自のテナント識別子メタデータが指定されているものは除きます)。次に例を示します。

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

14.2.1.4 コンテキスト・プロパティおよびキャッシュ範囲の構成

ランタイム・コンテキスト・プロパティは、マルチテナント戦略を実装するためにエンティティ(またはマップ済スーパークラス)のマルチテナント構成とともに使用されます。たとえば、テナント識別子列に割り当てられたエンティティ用のテナントIDは、実行時に、テナントが行およびデータベース表を所有しているか(またはアクセス)に応じて、(エンティティ・マネージャを介して)データにアクセスする際、制限を行うために使用されます。

マルチテナント・プロパティは、実行時に永続性ユニットの定義に指定するか、エンティティ・マネージャ・ファクトリ・コールの生成に渡すことができます。

テナント識別子列の優先順位は次のとおりです。

  1. EntityManager

  2. EntityManagerFactory

  3. アプリケーション・コンテキスト(Java EEコンテナにある場合)

たとえば、persistence.xmlの永続性ユニットで構成を設定するには次を実行します。

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

または、プログラミングを行いプロパティを設定するには、次を実行します。

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

注意:

ライブEntityManager時にテナントIDを切り替えることは許可されていません。


14.2.1.4.1 共有エンティティ・マネージャの構成

デフォルトで、テナントはエンティティ・マネージャ・ファクトリを共有しています。単一のアプリケーション・インスタンスで永続性ユニットのEntityManagerFactoryが共有されている場合、複数のテナントのリクエストを処理できます。

次の例では、エンティティ・マネージャ・ファクトリが共有されている場合の構成を示します。

EntityManager em = createEntityManager(MULTI_TENANT_PU);
em.getTransaction().begin();
em.setProperty(EntityManagerProperties.MULTITENANT_PROPERTY_DEFAULT, "my_id");

共有エンティティ・マネージャ・ファクトリを使用する際、デフォルトではL2キャッシュは共有されず、従ってマルチテナント・エンティティのキャッシュ設定はISOLATEDとなります。

キャッシュを共有するには、eclipselink.multitenant.tenants-share-cacheプロパティをtrueに設定します。これにより、マルチテナント・エンティティのキャッシュ設定は、PROTECTEDになります。


注意:

PROTECTEDの設定を使用している場合、キャッシュを使用する問合せは他のテナントのデータを返す場合があります。


14.2.1.4.2 非共有エンティティ・マネージャの構成

共有されないエンティティ・マネージャ・ファクトリを作成する場合、eclipselink.multitenant.tenants-share-emfプロパティはfalseに設定します。

エンティティ・マネージャ・ファクトリが共有されない場合、eclipselink.session-nameプロパティを使用し、次の例のようにセッション名を一意に指定する必要があります。これにより、テナントごとに一意のサーバー・セッションおよびキャッシュが提供されます。これにより、完全なキャッシュ機能が提供されます。次に例を示します。

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

もう1つ例を示します。

HashMap properties = new HashMap();
properties.put(PersistenceUnitProperties.MULTITENANT_SHARED_EMF, "false");
properties.put(PersistenceUnitProperties.SESSION_NAME, "non-shared-emf-for-this-emp");
properties.put(PersistenceUnitProperties.MULTITENANT_PROPERTY_DEFAULT, "this-emp");
...     
EntityManager em = Persistence.createEntityManagerFactory("multi-tenant-pu", properties).createEntityManager();

永続性ユニットの定義の例を示します。

<persistence-unit name="multi-tenant-pu">
  ...
  <properties>
    <property name="eclipselink.multitenant.tenants-share-emf" value="false"/>
    <property name="eclipselink.session-name" 
         value="non-shared-emf-for-this-emp"/>
    <property name="eclipselink.tenant-id" value="this-emp"/>
    ...
  </properties>
</persistence-unit>
14.2.1.4.3 エンティティ・マネージャの構成

エンティティ・マネージャ・レベルでプロパティを構成する際、テナントごとに同じサーバー・セッションを使用できるようキャッシュ戦略を指定する必要があります。たとえば、L2キャッシュに共有されているテナント情報がないようにするには、分離レベル(L1キャッシュ)を設定します。この設定は、エンティティ・マネージャ・ファクトリの作成時に設定されます。

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("multitenant", 
                      cacheProperties).createEntityManager(tenantProperties);
...

14.2.1.5 タスク4: 操作および問合せの実行

テナント識別子列はエンティティ・マネージャの操作時および問合せ時に使用されます。テナント識別子列と値は、次のエンティティ・マネージャの操作でサポートされます。

  • persist()

  • find()

  • refresh()

テナント識別子列と値は、次のエンティティ・マネージャの問合せでサポートされます。

  • 名前付き問合せ

  • すべて更新

  • すべて削除


注意:

マルチテナントは、名前付きのネイティブ問合せではサポートされません。マルチテナント環境で名前付きのネイティブ問合せを使用する場合は、マルチテナントに関する問題を問合せで直接に手動で処理してください。一般に、マルチテナント環境では名前付きのネイティブ問合せを使用しないことをお薦めします。


14.2.1.6 タスク5: 継承階層での単一表マルチテナントの使用

継承戦略は、継承タイプ(@javax.persistence.Inheritance)を指定して構成します。単一表マルチテナントは、次のように継承階層で使用できます。

  • SINGLE_TABLEまたはJOINED継承戦略の使用時には、マルチテナントのメタデータは、継承階層のルート・レベルでのみ適用できます。

  • TABLE_PER_CLASS継承階層でマルチテナントのメタデータを指定することも可能です。この場合、各エンティティはすべてのマッピング・データを含む独自の表を持つことになります(SINGLE_TABLEまたはJOINED戦略の場合は異なります)。その結果、TABLE_PER_CLASS戦略では、階層内の一部のエンティティがマルチテナントとなり、他はそうでなくなる場合があります。そのタイプのみを構築するためエンティティを単一の表に分離できないため、もう1つの継承戦略では、ルート・レベルでしかマルチテナントを指定できません。

14.3 テナントごとの表マルチテナントの使用

テナントごとの表マルチテナントでは、テナント独自の1つ以上の表に、アプリケーションの複数のテナントはデータを分離できます。複数のテナントの表は、名前に接頭辞または接尾辞が使用して区別して共有スキーマに含めるか、テナント固有の別のスキーマに含めることができます。テナントごとの表エンティティは、同じ永続性ユニットに他のタイプのマルチテナント・エンティティと混在させることができます。

テナントごとの表マルチテナント・タイプは次とともに使用します。

単一のアプリケーション・インスタンスで永続性ユニットのEntityManagerFactoryが共有されている場合、複数のテナントのリクエストを処理できます。

またはテナントごとに、別のEntityManagerFactoryインスタンスを使用することも可能です。(これはテナントごとに拡張を使用する場合に必要です。)この場合、テナント固有のスキーマおよび表明は、eclipselink-orm.xml構成ファイルに定義します。永続性ユニットにMetadataSourceを登録する必要があります。MetadataSourceは、アプリケーション外から提供されるその他の永続性ユニット・メタデータをサポートするために使用します。

MetadataSourceの詳細は、第13章「外部メタデータ・ソースの使用」を参照してください。『Oracle TopLink Java Persistence API (JPA)拡張機能リファレンス』metadata-sourceも参照してください。

テナントごとの表マルチテナント・タイプでは、個々のテナント表をエンティティ・レベルで使用することができます。トランザクションの開始後、各エンティティ・マネージャでテナントのコンテキスト・プロパティを提供する必要があります。

14.3.1 テナントごとの表マルチテナントを使用する場合の主なタスク

次のタスクでは、テナントごとの表マルチテナントを使用する手順を説明します。

14.3.1.1 タスク1: 前提条件

テナントごとの表マルチテナントを実装して使用するには、次が必要です。

14.3.1.2 タスク2: テナントごとの表マルチテナントの有効化

テナントごとの表マルチテナントは、@Multitenant注釈を使用するか、オブジェクト・リレーショナル・マッピング(ORM) XMLファイルで<multitenant>要素を使用するか、注釈とXMLの両方を使用して、宣言することにより有効にできます。

14.3.1.2.1 @Multitenantおよび@TenantTableDiscriminator注釈の使用

@Multitenant注釈を使用する場合は、@Entityまたは@MappedSuperclass注釈とともにこの注釈を指定して、TABLE_PER_TENANT属性を指定します。

次に例を示します。

@Entity
@Multitenant(TABLE_PER_TENANT
...)
public class Employee {
}

TABLE_PER_TENANT属性は、クライアントにエンティティと関連付けられている専用の表があることを指定します(TableおよびSecondaryTable)。

14.3.1.2.2 <multitenant>要素の使用

<multitenant>要素を使用する場合は、<entity>要素内にこの要素を指定します。次に例を示します。

<entity class="model.Employee">
   <multitenant type="TABLE_PER_TENANT">
   ...
   </multitenant>
   ...
</entity>

14.3.1.3 タスク3: テナント表識別子の指定

テナント表識別子は、テナントごとの表マルチテナント戦略で使用する表識別子の種類を示します。テナント表識別子は、プロパティで指定します。独自の識別子を定義するか、デフォルトのorg.eclipse.persistence.config.PersistenceUnitProperties.MULTITENANT_PROPERTY_DEFAULT = "eclipselink.tenant-id"プロパティを使用します。

テナント表識別子はエンティティまたはマップ済のスーパークラス・レベルで指定でき、Multitenant(TABLE_PER_TENANT)の指定が常に必要です。テナント表識別子のみの指定では不十分です。

テナント表識別子は、関連付けられているアプリケーション・コンテキストとともに使用して、どの表にアプリケーション・テナントがアクセスできるかを示します。

14.3.1.3.1 @TenantTableDiscriminator注釈の使用

@TenantTableDiscriminator注釈を使用し、表とテナントの関連付けを指定します。テナント表識別子には、種類とコンテキスト・プロパティを含む必要があります。

  • type属性を使用し、使用する識別子の種類を指定します。

    • テナント表識別子を接頭辞としてすべてのマルチテナント表に適用するには、PREFIXを使用します。

    • テナント表識別子を接尾辞としてすべてのマルチテナント表に適用するには、SUFFIXを使用します。

    • テナント表識別子をスキーマとしてすべてのマルチテナント表に適用するには、SCHEMAを使用します。この戦略では、データベースを正しくプロビジョニングする必要があります。

  • contextProperty属性を使用し、ユーザーを識別します。コンテキスト・プロパティの値は、ユーザーを識別するテナントIDです。これは、エンティティ・マネージャに構成するか、永続性ユニットごとにテナントごとの表を分離する場合は、エンティティ・マネージャ・ファクトリに構成します。

次に例を示します。

@Entity
@Table(name=”EMP”)
@Multitenant(TABLE_PER_TENANT)
@TenantTableDiscriminator(type=SCHEMA, contextProperty="eclipselink-tenant.id")
public class Employee {
    ...
}
14.3.1.3.2 <tenant-table-discriminator>要素の使用

<tenant-table-discriminator>要素を使用する場合は、<multitenant>要素内にこの要素を指定して、nameおよびcontext-property属性を指定します。次に例を示します。

<entity class="Employee">
  <multitenant type="TABLE_PER_TENANT">
    <tenant-table-discriminator type="SCHEMA"  
        context-property="eclipselink-tenant.id"/>
  </multitenant>
  <table name="EMP">
  ...
</entity>

14.3.1.4 タスク4: 実行時のコンテキスト・プロパティの指定

実行時に、エンティティ・マネージャ・ファクトリに渡される永続性ユニットの定義を使用するコンテキスト・プロパティを指定するか、個々にエンティティ・マネージャに設定して、コンテキスト・プロパティ構成を指定できます。次に例を示します。

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

プログラミングを行い、実行時にコンテキスト・プロパティを指定する手順は次のとおりです。

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

エンティティ・マネージャのプロパティ定義は、次のとおりです。

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

14.3.1.5 タスク5: 操作および問合せの実行

テナント識別子列はエンティティ・マネージャの操作時および問合せ時に使用されます。テナント識別子列と値は、次のエンティティ・マネージャの操作でサポートされます。

  • persist()

  • find()

  • refresh()

テナント識別子列と値は、次のエンティティ・マネージャの問合せでサポートされます。

  • 名前付き問合せ

  • すべて更新

  • すべて削除


注意:

マルチテナントは、名前付きのネイティブ問合せではサポートされません。マルチテナント環境で名前付きのネイティブ問合せを使用する場合は、マルチテナントに関する問題を問合せで直接に手動で処理してください。一般に、マルチテナント環境では名前付きのネイティブ問合せを使用しないことをお薦めします。


14.4 VPDマルチテナントの使用

仮想プライベート・データベース(VPD)では、様々なパラメータに基づくセキュリティ・コントロールを使用して、データベース・オブジェクトへのアクセスを制限します。

たとえば、Oracle仮想プライベート・データベースでは、行および列レベルでデータベースへのアクセスを制限するセキュリティ・ポリシーをサポートしています。セキュリティ・ポリシーが適用された表、ビュー、シノニムに対して発行されたSQL文に対し、Oracle VPDでは、動的なWHERE句を追加します。

Oracle仮想プライベート・データベースでは、データベース表、ビューまたはシノニムに対し、セキュリティが直接強制されます。セキュリティ・ポリシーはこれらのデータベース・オブジェクトに直接付加され、ユーザーがデータにアクセスするたびにポリシーは自動的に適用されるため、セキュリティは回避できません。

ユーザーがOracle仮想プライベート・データベース・ポリシーで保護されている表、ビューまたはシノニムに直接的または間接的にアクセスすると、Oracle DatabaseはユーザーのSQL文を動的に変更します。この変更は、セキュリティ・ポリシーを実装する関数によって戻されたWHERE条件(述語)に基づいて行われます。Oracle仮想プライベート・データベースでは、関数内に記述された条件、または関数が戻す条件を使用して、動的に、またユーザーに対して透過的に文を変更します。Oracle仮想プライベート・データベースのポリシーは、SELECT、INSERT、UPDATE、INDEXおよびDELETE文に適用できます。

EclipseLink VPDマルチテナントを使用する場合、データベースでは、SELECT、INSERT、UPDATE、INDEXおよびDELETEのすべての問合せでテナント・フィルタリングを行います。

EclipseLink VPDマルチテナントを使用するには、@Multitenantおよび@TenantDiscriminatorColumnを使用した次の例のとおり、まずデータベースでVPDを構成し、その後エンティティまたはマップ済のスーパークラスでマルチテナントを指定する必要があります。

14.4.1 VPDマルチテナントを使用する場合の主なタスク

次のタスクでは、Oracle仮想プライベート・データベースでVPDマルチテナントを使用する手順を説明します。

14.4.1.1 タスク1: 前提条件

VPDマルチテナントを実装し使用するには、次が必要です。

14.4.1.2 タスク2: 仮想プライベート・データベースの構成

この例では、Oracle仮想プライベート・データベースはポリシーとストアド・プロシージャで構成されています。ポリシーとは、問合せの結果を制限するストアド関数を使用するよう、データベースに通知するデータベース・コールです。この例の関数は、ident_funcといい、SCOTT.TASK表に対してSELECTUPDATEまたはDELETEが実行されると常に実行されます。ポリシーは次のように生成します。

CALL DBMS_RLS.ADD_POLICY ('SCOTT', 'TASK', 'todo_list_policy', 'SCOTT', 'ident_func', 'select, update, delete'));

次で定義する関数は、VPDが接続に渡される識別子に基づいてデータを制限するために使用されます。この関数では、表のUSER_ID列を使用して行を制限しています。行は、userenvコンテキストのclient_identifier変数に何が設定されているかに基づいて制限されます。

CREATE OR REPLACE FUNCTION ident_func (p_schema IN VARCHAR2 DEFAULT NULL, p_object IN VARCHAR2 DEFAULT NULL) 
    RETURN VARCHAR2 
    AS 
    BEGIN 
       RETURN 'USER_ID = sys_context(''userenv'', ''client_identifier'')';
    END;

14.4.1.3 タスク3: エンティティまたはマップ済スーパークラスの構成

前述のとおり、VPDは行へのアクセスを制限するためにUSER_ID列を使用するよう構成されています。したがって、挿入時には、EclipseLinkにUSER_ID列を自動移入するよう通知する必要があります。次のコードでは、EclipseLinkのマルチテナントが使用され、tenant.idというプロパティを使用して、エンティティ・マネージャにクライアント識別子が渡されることが指定されています。フィルタリングはVPDがデータベースに対して行うので、このエンティティでキャッシュを無効にしてユーザー間でリークが発生するのを避ける必要があります。

@Entity
@Multitenant(VPD)
@TenantDiscriminatorColumn(name = "USER_ID", contextProperty = "tenant.id")
@Cacheable(false)
 
public class Task implements Serializable {
...
...

14.4.1.4 タスク4: 基準生成の無効化

単一表およびテナントごとの表マルチテナントが有効な場合、生成されるすべてのSQLにクライアント識別子が自動的に付加されます。ただし、VPDを使用してデータへのアクセスを制限する場合は、識別子が自動的に付加されないようにする必要があります。

TopLink 12c (12.1.2.0.0)以上では、次のように基準の生成を無効にします。

14.4.1.5 タスク5: persistence.xmlの構成

persistence.xmlに次のプロパティを追加します。

次を含め、VPD識別子を設定およびクリアします。

<property name="eclipselink.session-event-listener" value="example.VPDSessionEventAdapter" />

次を含め、エンティティ・マネージャごとに接続を1つ用意します。

<property name="eclipselink.jdbc.exclusive-connection.mode" value="Always" /> 

次を含め、EclipseLinkからネイティブ問合せを実行できるようにします。これは、VPDアーチファクトの生成に必要です。

<property name="eclipselink.jdbc.allow-native-sql-queries" value="true" />
</properties>

次に例を示します。

 <properties>
   <property name="eclipselink.session-event-listener" value="example.VPDSessionEventAdapter" />
   <property name="eclipselink.jdbc.exclusive-connection.mode" value="Always" /> 
   <property name="eclipselink.jdbc.allow-native-sql-queries" value="true" />
  ...
</properties>

14.5 その他の参考資料

この章のソリューションが実装されているその他のテクノロジおよびツールの詳細は、次の参考資料を参照してください。