この章では、Oracle TopLinkでアプリケーションを1つ開発し、それを別の複数のクライアント(つまりテナント)に、アプリケーションの割合、データの分離度およびテナントに固有の機能を変えてデプロイする方法について説明します。たとえば、規模の大きな企業が1つの給与アプリケーションを、複数の部門で使用するよう開発したとします。各部門は、独自のデータと共有データにアクセスできますが、他の部門のデータはまったく参照することができません。
この章の内容は次のとおりです。
ユース・ケース
Saas環境などで、アプリケーションの複数のクライアントで、データ・ソースを共有しながら、自分のデータにはプライベートにアクセスできる必要があります。
解決方法
テナントを分離する戦略を決定し、TopLinkのテナントの分離機能を使用してその戦略を導入します。
コンポーネント
TopLink 12c (12.1.2.0.0)以上。
注意: TopLinkのコア機能は、オープン・ソースのEclipse Foundationの永続性フレームワークであるEclipseLinkによって提供されています。EclipseLinkでは、Java Persistence API (JPA)、Java Architecture for XML Binding (JAXB)、および標準に基づいたその他の永続性テクノロジと、それらの標準の拡張が実装されます。TopLinkには、EclipseLinkのすべてに加え、Oracleの追加機能が含まれています。 |
Oracle Database、Oracle Express、MySQLなどの対応するJava Database Connectivity (JDBC)データベース。
EclipseLinkでは、テナントを分離する機能の設計および実装をかなり柔軟にできます。次などが可能です。
アプリケーションの分離オプション
コンテナ/サーバーの分離
同じコンテナ/サーバー内でアプリケーションを分離
同じアプリケーション内でエンティティ・マネージャ・ファクトリおよび共有キャッシュを分離
エンティティ・マネージャごとにテナントは分離し、エンティティ・マネージャ・ファクトリは共有
データの分離オプション
データベースの分離
スキーマ/表領域の分離
表の分離
表は共有し列を分離
問合せのフィルタリング
Oracle仮想プライベート・データベース(VPD)
EclipseLinkには、データ・ソースでマルチテナンシを提供する次のオプションがあります。
単一表マルチテナントでは、テナントは表を共有できます。各テナントは識別子列によって識別される独自の行を持ち、それらの列は他のテナントは参照できません。「単一表マルチテナントの使用」を参照してください。
テナントごとの表マルチテナントでは、各テナントは表テナント識別子で識別される独自の表を持ち、それらの表は他のユーザーは参照できません。「テナントごとの表マルチテナントの使用」を参照してください。
(VDP)マルチテナントでは、テナントは同じ表を共有する複数のテナントをサポートする機能を持つVDPデータベースを使用します。「VPDマルチテナントの使用」を参照してください。
EclipseLinkでは、拡張可能なエンティティおよびMetadataSource
を使用した拡張可能なエンティティを介したテナントに固有の拡張も提供しています。それらの機能の詳細は、第12章「JPAエンティティおよびJAXB Beanの拡張可能化」および第13章「外部メタデータ・ソースの使用」を参照してください。
単一表マルチテナントでは、エンティティまたはマップ済のスーパークラスがマップされるすべての表(Table
またはSecondaryTable
)に、複数のテナント用の行を含めることができます。テナントに固有の行へのアクセスは、指定のテナントに制限されています。
テナント固有の行は、1つ以上のテナント識別子列を使用してテナントに関連付られています。識別子列には、どのような永続性コンテンツがアクセスできるか制限するアプリケーション・コンテキスト値が使用されています。
マップした表に対する問合せの結果は、プロパティ値として提供されたテナントの識別子の値に制限されます。これは、表でのすべてのinsert、updateおよびdelete操作に該当します。マルチテナントのメタデータがマップ済のスーパークラス・レベルで適用されると、独自のマルチテナント・メタデータを指定しないかぎり、すべてのサブエンティティに適用されます。
注意: 単一表マルチテナントのコンテキストで、単一表は、複数のテナントが1つの表を共有し、各テナントのデータは識別子列で他のテナントのデータから区別されていることを意味します。単一表マルチテナントでは、複数の表を使用することも可能ですが、その場合は、エンティティの永続性データは複数の表に保存され、複数のテナントはすべての表を共有できます。 |
単一表マルチテナントを使用する場合には、次のテスクを実行します。
単一表マルチテナントを実装するには、次が必要です。
TopLink 12c (12.1.2.0.0)以上。
http://www.oracle.com/technetwork/middleware/toplink/downloads/index.html
からTopLinkをダウンロードします。
Oracle Database、Oracle Database Express Edition (Oracle Database XE)またはMySQLを含む対応するすべてのJava Database Connectivity (JDBC)データベース。ここで説明する手順は、Oracle Database XE 11gリリース2のものです。
動作保証マトリックスについては、http://www.oracle.com/technetwork/middleware/ias/index-099524.html
を参照してください。
単一表マルチテナントは、@Multitenant
注釈を使用するか、オブジェクト・リレーショナル・マッピング(ORM) XMLファイルで<multitenant>
要素を使用するか、注釈とXMLの両方を使用して、宣言することにより有効にできます。
@Multitenant
注釈を使用する場合、@Entity
または@MappedSuperclass
注釈に含めます。次に例を示します。
@Entity @Table(name=”EMP”) @Multitenant(SINGLE_TABLE) public class Employee { }
注意: 単一表はマルチテナントのデフォルトのタイプなので、 |
注意: 識別子列はプライマリ表にあることが前提となっているので |
識別子列は、関連付けられているアプリケーション・コンテキストとともに使用して、表のどの行にアプリケーション・テナントがアクセスできるかを示します。
テナント識別子列は、@TenantDiscriminatorColumn
注釈を指定するか、オブジェクト・リレーショナル(ORM) XMLファイルで<tenant-discriminator-column>
要素を使用して、宣言することにより指定できます。
識別子列には、次の特徴があります。
テナントの識別子列を使用する場合、常に@Multitenant
(またはORM XMLファイルで<multitenant>
)も使用する必要があります。テナント識別子列のみの指定はできません。
テナント識別子列は、別の表が明示的に指定されている場合を除きプライマリ表上にあることが前提となっています。
永続性では、テナント識別子列の値はそれに関連付られているコンテンツのプロパティから移入されます。
マルチテナント・エンティティが指定されている場合、テナント識別子列はデフォルトのものとなります。デフォルト値は次のとおりです。
Name = TENANT_ID
(データベースの列名)
Context property = eclipselink.tenant.id
(データベース列の移入に使用されるコンテキスト・プロパティ)
テナント識別子列はアプリケーションで定義できます。つまり、各共有エンティティ表で、識別子列は特定の列には関連付られていません。TENANT_ID
やT_ID
などを使用できます。
アプリケーションで定義できるテナント識別子列の数には制限はありません。
識別子列には、任意の名前を使用できます。
生成されるスキーマには、指定したテナント識別子列などが含まれます。
テナント識別子列は、マッピングすることもマッピング解除することもできます。
テナント識別子列がマップされたら、関連付られているマッピング属性は読取り専用としてマークする必要があります。
マップおよびマップ解除されている両方のプロパティは、SELECT問合せの発行時に追加の基準を形成するために使用します。
@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() { ... }
<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>
テナント識別子列は、主キーまたは別の列にマッピングできます。次の例では、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>
識別子列は、エンティティおよびマップ済スーパークラス・レベルのみでなく、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>
ランタイム・コンテキスト・プロパティは、マルチテナント戦略を実装するためにエンティティ(またはマップ済スーパークラス)のマルチテナント構成とともに使用されます。たとえば、テナント識別子列に割り当てられたエンティティ用のテナントIDは、実行時に、テナントが行およびデータベース表を所有しているか(またはアクセス)に応じて、(エンティティ・マネージャを介して)データにアクセスする際、制限を行うために使用されます。
マルチテナント・プロパティは、実行時に永続性ユニットの定義に指定するか、エンティティ・マネージャ・ファクトリ・コールの生成に渡すことができます。
テナント識別子列の優先順位は次のとおりです。
EntityManager
EntityManagerFactory
アプリケーション・コンテキスト(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();
注意: ライブ |
デフォルトで、テナントはエンティティ・マネージャ・ファクトリを共有しています。単一のアプリケーション・インスタンスで永続性ユニットの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になります。
注意:
|
共有されないエンティティ・マネージャ・ファクトリを作成する場合、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>
エンティティ・マネージャ・レベルでプロパティを構成する際、テナントごとに同じサーバー・セッションを使用できるようキャッシュ戦略を指定する必要があります。たとえば、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); ...
テナント識別子列はエンティティ・マネージャの操作時および問合せ時に使用されます。テナント識別子列と値は、次のエンティティ・マネージャの操作でサポートされます。
persist()
find()
refresh()
テナント識別子列と値は、次のエンティティ・マネージャの問合せでサポートされます。
名前付き問合せ
すべて更新
すべて削除
注意: マルチテナントは、名前付きのネイティブ問合せではサポートされません。マルチテナント環境で名前付きのネイティブ問合せを使用する場合は、マルチテナントに関する問題を問合せで直接に手動で処理してください。一般に、マルチテナント環境では名前付きのネイティブ問合せを使用しないことをお薦めします。 |
継承戦略は、継承タイプ(@javax.persistence.Inheritance
)を指定して構成します。単一表マルチテナントは、次のように継承階層で使用できます。
SINGLE_TABLE
またはJOINED
継承戦略の使用時には、マルチテナントのメタデータは、継承階層のルート・レベルでのみ適用できます。
TABLE_PER_CLASS
継承階層でマルチテナントのメタデータを指定することも可能です。この場合、各エンティティはすべてのマッピング・データを含む独自の表を持つことになります(SINGLE_TABLE
またはJOINED
戦略の場合は異なります)。その結果、TABLE_PER_CLASS
戦略では、階層内の一部のエンティティがマルチテナントとなり、他はそうでなくなる場合があります。そのタイプのみを構築するためエンティティを単一の表に分離できないため、もう1つの継承戦略では、ルート・レベルでしかマルチテナントを指定できません。
テナントごとの表マルチテナントでは、テナント独自の1つ以上の表に、アプリケーションの複数のテナントはデータを分離できます。複数のテナントの表は、名前に接頭辞または接尾辞が使用して区別して共有スキーマに含めるか、テナント固有の別のスキーマに含めることができます。テナントごとの表エンティティは、同じ永続性ユニットに他のタイプのマルチテナント・エンティティと混在させることができます。
テナントごとの表マルチテナント・タイプは次とともに使用します。
識別子の種類を指定するテナント表識別子(接頭辞または接尾辞付きのスキーマまたは名前)
ユーザーを識別するテナントID(永続性ユニットごとにテナントごとの表を分離する場合、エンティティ・マネージャごとに構成するか、エンティティ・マネージャ・ファクトリで構成)
単一のアプリケーション・インスタンスで永続性ユニットのEntityManagerFactory
が共有されている場合、複数のテナントのリクエストを処理できます。
またはテナントごとに、別のEntityManagerFactory
インスタンスを使用することも可能です。(これはテナントごとに拡張を使用する場合に必要です。)この場合、テナント固有のスキーマおよび表明は、eclipselink-orm.xml
構成ファイルに定義します。永続性ユニットにMetadataSource
を登録する必要があります。MetadataSource
は、アプリケーション外から提供されるその他の永続性ユニット・メタデータをサポートするために使用します。
MetadataSource
の詳細は、第13章「外部メタデータ・ソースの使用」を参照してください。『Oracle TopLink Java Persistence API (JPA)拡張機能リファレンス』のmetadata-source
も参照してください。
テナントごとの表マルチテナント・タイプでは、個々のテナント表をエンティティ・レベルで使用することができます。トランザクションの開始後、各エンティティ・マネージャでテナントのコンテキスト・プロパティを提供する必要があります。
エンティティの表(Table
およびSecondaryTable
)は、テナントのコンテキストに基づく、テナントの個別の表です。結合またはコレクション表を使用するエンティティ内のリーションシップも、テナントごとの表のコンテキスト内に存在することが前提となっています。
マルチテナントのメタデータは、SINGLE_TABLE
またはJOINED
継承戦略の使用時には、継承階層のルート・レベルでしか適用できません。マルチテナント・メタデータは、TABLE_PER_CLASS
継承階層内に指定できます。
次のタスクでは、テナントごとの表マルチテナントを使用する手順を説明します。
テナントごとの表マルチテナントを実装して使用するには、次が必要です。
TopLink 12c (12.1.2.0.0)以上。
http://www.oracle.com/technetwork/middleware/toplink/downloads/index.html
からTopLinkをダウンロードします。
Oracle Database、Oracle Database Express Edition (Oracle Database XE)またはMySQLを含む対応するすべてのJava Database Connectivity (JDBC)データベース。ここで説明する手順は、Oracle Database XE 11gリリース2のものです。
動作保証マトリックスについては、http://www.oracle.com/technetwork/middleware/ias/index-099524.html
を参照してください。
テナントごとの表マルチテナントは、@Multitenant
注釈を使用するか、オブジェクト・リレーショナル・マッピング(ORM) XMLファイルで<multitenant>
要素を使用するか、注釈とXMLの両方を使用して、宣言することにより有効にできます。
@Multitenant
注釈を使用する場合は、@Entity
または@MappedSuperclass
注釈とともにこの注釈を指定して、TABLE_PER_TENANT
属性を指定します。
次に例を示します。
@Entity @Multitenant(TABLE_PER_TENANT ...) public class Employee { }
TABLE_PER_TENANT
属性は、クライアントにエンティティと関連付けられている専用の表があることを指定します(Table
およびSecondaryTable
)。
テナント表識別子は、テナントごとの表マルチテナント戦略で使用する表識別子の種類を示します。テナント表識別子は、プロパティで指定します。独自の識別子を定義するか、デフォルトのorg.eclipse.persistence.config.PersistenceUnitProperties.MULTITENANT_PROPERTY_DEFAULT = "eclipselink.tenant-id"
プロパティを使用します。
テナント表識別子はエンティティまたはマップ済のスーパークラス・レベルで指定でき、Multitenant(TABLE_PER_TENANT)
の指定が常に必要です。テナント表識別子のみの指定では不十分です。
テナント表識別子は、関連付けられているアプリケーション・コンテキストとともに使用して、どの表にアプリケーション・テナントがアクセスできるかを示します。
@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 { ... }
<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>
実行時に、エンティティ・マネージャ・ファクトリに渡される永続性ユニットの定義を使用するコンテキスト・プロパティを指定するか、個々にエンティティ・マネージャに設定して、コンテキスト・プロパティ構成を指定できます。次に例を示します。
<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"); ...
テナント識別子列はエンティティ・マネージャの操作時および問合せ時に使用されます。テナント識別子列と値は、次のエンティティ・マネージャの操作でサポートされます。
persist()
find()
refresh()
テナント識別子列と値は、次のエンティティ・マネージャの問合せでサポートされます。
名前付き問合せ
すべて更新
すべて削除
注意: マルチテナントは、名前付きのネイティブ問合せではサポートされません。マルチテナント環境で名前付きのネイティブ問合せを使用する場合は、マルチテナントに関する問題を問合せで直接に手動で処理してください。一般に、マルチテナント環境では名前付きのネイティブ問合せを使用しないことをお薦めします。 |
仮想プライベート・データベース(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を構成し、その後エンティティまたはマップ済のスーパークラスでマルチテナントを指定する必要があります。
次のタスクでは、Oracle仮想プライベート・データベースでVPDマルチテナントを使用する手順を説明します。
VPDマルチテナントを実装し使用するには、次が必要です。
TopLink 12c (12.1.2.0.0)以上。
http://www.oracle.com/technetwork/middleware/toplink/downloads/index.html
からTopLinkをダウンロードします。
Oracle仮想プライベート・データベースなど、すべての対応するVDPをサポートするJava Database Connectivity (JDBC)データベース。
動作保証マトリックスについては、http://www.oracle.com/technetwork/middleware/ias/index-099524.html
を参照してください。
この例では、Oracle仮想プライベート・データベースはポリシーとストアド・プロシージャで構成されています。ポリシーとは、問合せの結果を制限するストアド関数を使用するよう、データベースに通知するデータベース・コールです。この例の関数は、ident_func
といい、SCOTT.TASK
表に対してSELECT
、UPDATE
または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;
前述のとおり、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 { ... ...
単一表およびテナントごとの表マルチテナントが有効な場合、生成されるすべてのSQLにクライアント識別子が自動的に付加されます。ただし、VPDを使用してデータへのアクセスを制限する場合は、識別子が自動的に付加されないようにする必要があります。
TopLink 12c (12.1.2.0.0)以上では、次のように基準の生成を無効にします。
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>
この章のソリューションが実装されているその他のテクノロジおよびツールの詳細は、次の参考資料を参照してください。
コード例
『Oracle TopLink Java Persistence API (JPA)拡張機能リファレンス』で次を参照してください。
@Multitenant
@TenantDiscriminatorColumn
@TenantDiscriminatorColumns
@TenantTableDiscriminator