この章では、Software as a Service (SaaS)環境で動作する共有TopLinkアプリケーションを作成する手順を説明します。
この章では、次の項目について説明します。
SaaS用のOracleプラットフォームには、Oracle Fusion Middlewareの一部であるTopLinkが含まれます。これにより、SaaSアプリケーションのビルド、デプロイおよび管理が可能になっています。TopLinkでは、クラウド対応のアプリケーションおよびサービスで永続性を管理できます。高いパフォーマンスとスケーラビリティを維持しながら、マルチテナントおよび拡張性に対処する、より柔軟なSaaSソリューションを開発するには、これらのアプリケーションの永続性レイヤーが重要なコンポーネントになります。
TopLinkでは、拡張性、マルチテナント、および外部メタデータ・ソースの使用をサポートすることにより、サービスとしてのソフトウェアの提供をサポートしており、次の項に、その詳細が説明されています。
@VirtualAccessMethods
注釈を使用して、エンティティが拡張可能であることを指定します。拡張可能なエンティティで仮想プロパティを使用すれば、エンティティの外部でマッピングを指定できます。これにより、エンティティのソース・ファイルの変更およびエンティティの永続性ユニットの再デプロイをしなくても、マッピングを変更できるようになります。
拡張可能なエンティティは、複数のクライアント(テナント)が共有の汎用アプリケーションを使用できるマルチテナント(またはSaaS)環境で使用するのに役立ちます。テナントは、自分のデータにプライベートにアクセスでき、他のテナントと共有するデータにもアクセスできます。
拡張可能なエンティティを使用すれば、次のことを行えます。
一部のマッピングがすべてのユーザーに共通で、一部のマッピングはユーザー固有であるようなアプリケーションを作成できます。
顧客に提供された後のアプリケーションにマッピングを追加できます(デプロイメント後でも可能)。
マッピング変更後も、同じEntityManagerFactory
を使用してデータを操作できます。
アプリケーションで使用されるメタデータのソースを追加提供できます。
拡張可能なJPAエンティティを作成およびサポートするには:
エンティティの構成では、@VirtualAccessMethods
によるエンティティ・クラスの注釈指定、プロパティ値に対するget
およびset
メソッドの追加、および拡張された属性と値をストアするデータ構造の追加をします。
@VirtualAccessMethods
でエンティティに注釈を付けて、エンティティが拡張可能であることを指定し、仮想プロパティを定義します。
表6-1は、@VirtualAccessMethods
注釈で使用可能な属性を説明しています。
エンティティに対して、get(String)
およびset(String, Object)
メソッドを追加します。get()
メソッドは、プロパティ名によって値を返し、set()
メソッドはプロパティ名によって値をストアします。これらのメソッドのデフォルト名は、get
およびset
で、@VirtualAccessMethods
注釈でオーバーライドできます。
EclipseLinkでは、ウィービングが有効な場合に、これらのメソッドがウィービングされて、遅延ロード、変更追跡、フェッチ・グループおよび内部最適化がサポートされます。get(String)
およびset(String, Object)
シグネチャを使用する必要があります。そうしないと、ウィービングは動作しません。
注意: OneToOneマッピングで仮想アクセス・メソッドを使用する場合、ウィービングはサポートされません。使用すると、例外がスローされます。 |
拡張された属性と値、つまり仮想マッピングをストアするデータ構造を追加します。その後、これらをデータベースにマップできます。6.2.1.3項「タスク3: 追加マッピングの提供」を参照してください。
仮想マッピングは(例6-1に示されているような)Map
にストアするのが一般的ですが、他の方式を使用することもできます。たとえば、ディレクトリ・システムに仮想マッピングをストアすることもできます。
フィールドベースのアクセスを使用する際には、@Transient
でデータ構造に注釈を付けて、この構造を別のマッピングに使用できないようにします。プロパティベースのアクセスを使用する場合、@Transient
は不要です。
例6-1に、プロパティ・アクセスを使用するエンティティ・クラスを示します。
例6-1 プロパティ・アクセスを使用するエンティティ・クラス
@Entity
@VirtualAccessMethods
public class Customer{
@Id
private int id;
...
@Transient
private Map<String, Object> extensions;
public <T> T get(String name) {
return (T) extentions.get(name);
}
public Object set(String name, Object value) {
return extensions.put(name, value);
}
フレキシブルなマッピング・データを保存するために、余分な列があるデータベース表を用意します。たとえば、次のCustomer
表には、事前定義された2つの表、ID
おびNAME
があり、3つのフレキシブル列、FLEX_COL1
、FLEX_COL2
、FLEX_COL3
があります。
CUSTOMER
INTEGER
ID
VARCHAR
NAME
VARCHAR
FLEX_COL1
VARCHAR
FLEX_COL2
VARCHAR
FLEX_COL3
次に、「タスク3: 追加マッピングの提供」に説明されているように、拡張された属性を永続化するために、どのフレキシブル列を使用するかを指定できます。
追加マッピングを提供するには、column
およびaccess-methods
属性が指定されたマッピングをeclipselink-orm.xml
ファイルに追加します。次に例を示します。
<basic name="idNumber" attribute-type="String"> <column name="FLEX_COL1"/> <access-methods get-method="get" set-method="set"/> </basic>
永続性ユニット・プロパティを構成して、アプリケーションがeclipselink-orm.xml
ファイルからフレキシブル・マッピングを取得する必要があることを示します。次の項に説明されているように、persistence.xml
を使用するか、EntityManagerFactory
にプロパティを設定して、永続性ユニット・プロパティを設定できます。
外部マッピングの詳細は、EclipseLinkドキュメントの『External Mappings』を参照してください。
http://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Advanced_JPA_Development/External_Mappings
デフォルトのeclipselink-orm.xml
ファイルを使用するには、persistence.xml
ファイルでeclipselink.metadata-source
プロパティを使用します。指定した場所にある別のファイルを使用するには、eclipselink.metadata-source.xml.url
プロパティを使用します。次に例を示します。
<property name="eclipselink.metadata-source" value="XML"/> <property name="eclipselink.metadata-source.xml.url" value="foo://bar"/>
拡張機能は、ブートストラップ時にメタデータ・リポジトリにアクセスして追加されます。メタデータ・リポジトリには、そこに保持されているメタデータを取得するメソッドを提供するクラスを介してアクセスします。現在のリリースには、XMLリポジトリをサポートするメタデータ・リポジトリの実装が含まれています。
メタデータ・リポジトリに対して使用するクラスおよび構成情報を永続性ユニット・プロパティに指定します。エンティティ・マネージャ・ファクトリでは、メタデータ・リポジトリの追加マッピング情報が、ブートストラップに使用されるメタデータに統合されます。
独自のクラスの実装を用意して、メタデータ・リポジトリにアクセスできます。各メタデータ・リポジトリ・アクセス・クラスには、リポジトリに接続するために使用するプロパティのセットを個別に指定する必要があります。
次のクラスのどちらかをサブクラスにできます。
org.eclipse.persistence.internal.jpa.extensions.MetadataRepository
org.eclipse.persistence.internal.jpa.extensions.XMLMetadataRepository
次の例では、com.foo
で始まるプロパティが、開発者によって定義されています。
<property name="eclipselink.metadata-source" value="com.foo.MetadataRepository"/> <property name="com.foo.MetadataRepository.location" value="foo://bar"/> <property name="com.foo.MetadataRepository.extra-data" value="foo-bar"/>
例6-2は、次のことを示しています。
非拡張フィールドには、フィールド・アクセスが使用されています。
拡張フィールドには、デフォルト(get(String)
およびset(String, Object)
)を使用した仮想アクセスが使用されています。
get(String)
およびset(String, Object)
メソッドは、マッピングで使用されていなくても、@VirtualAccessMethods
が指定されているので、ウィービングされます。
拡張機能は、@Transient
を指定して、移植可能な方法でマッピングされています。
例6-2 デフォルトのgetおよびsetメソッド名を使用した仮想アクセス
@Entity@VirtualAccessMethods
public class Address { @Id private int id;@Transient
private Map<String, Object> extensions; public int getId(){ return id; }public <T> T get(String name) {
return (T) extentions.get(name);
}public Object set(String name, Object value) {
return extensions.put(name, value);
} ...
例6-3は、次のことを示しています。
非拡張フィールドには、フィールド・アクセスが使用されています。
getおよびsetに使用されるメソッドを@VirtualAccessMethods
注釈でオーバーライドしています。
get(String)
およびset(String, Object)
メソッドは、マッピングで使用されていなくても、@VirtualAccessMethods
が指定されているので、ウィービングされます。
拡張機能は、@Transient
を指定して、移植可能な方法でマッピングされています。
get()
およびset()
メソッドのどちらを使用するかを、拡張マッピングのXMLで示しています。
例6-3 GetおよびSetメソッドのオーバーライド
@Entity@VirtualAccessMethods(get="getExtension", set="setExtension")
public class Address { @Id private int id;@Transient
private Map<String, Object> extensions; public int getId(){ return id; }public <T> T getExtension(String name) {
return (T) extensions.get(name);
}public Object setExtension(String name, Object value) {
return extensions.put(name, value);
} ...<basic name="name" attribute-type="String">
<column name="FLEX_1"/>
<access-methods get-method="getExtension" set-method="setExtension"/>
</basic>
例6-4は、次のことを示しています。
非拡張フィールドには、プロパティ・アクセスが使用されています。
拡張フィールドには、デフォルト(get(String)
およびset(String, Object)
)を使用した仮想アクセスが使用されています。
拡張機能は移植可能な方法でマッピングされています。プロパティ・アクセスが使用されるので、@Transient
は不要です。
get(String)
およびset(String, Object)
メソッドは、マッピングで使用されていなくても、@VirtualAccessMethods
が指定されているので、ウィービングされます。
例6-4 プロパティ・アクセスの使用
@Entity @VirtualAccessMethods public class Address { private int id; private Map<String, Object> extensions; @Id public int getId(){ return id; } public <T> T get(String name) {return (T) extensions.get(name);
}public Object set(String name, Object value) {
return extensions.put(name, value);
} ...
@XmlVirtualAccessMethods
注釈を使用して、JAXB Beanが拡張可能であることを指定します。拡張可能なBeanで仮想プロパティを使用すれば、Beanの外部でマッピングを指定できます。これにより、Beanのソース・ファイルの変更およびBeanの永続性ユニットの再デプロイをしなくても、マッピングを変更できるようになります。
マルチテナント(またはSaaS)アーキテクチャでは、サーバーで1つのアプリケーションが動作して、複数のクライアント組織(テナント)にサービスを提供します。優れたマルチテナント・アプリケーションでは、テナントごとのカスタマイズが可能です。これらのカスタマイズがデータに対して行われる場合は、バインディング・レイヤーでカスタマイズを処理するのが難しい場合があります。JAXBは、実際のフィールドおよびプロパティがあるドメイン・モデルを操作するように設計されています。EclipseLink Object-XML 2.3 (MOXyとも呼ばれます)には、このユースケースを容易に処理できる、仮想プロパティの概念が導入されています。仮想プロパティはObject-XMLメタデータ・ファイルによって定義されているので、ソースを変更しなくてもクラスを拡張できます。
この項の内容は、次のとおりです。
拡張可能なJAXB Beanを作成およびサポートするには:
Beanの構成では、@XmlVirtualAccessMethods
によるBeanクラスの注釈指定、プロパティ値に対するget
およびset
メソッドの追加、および拡張された属性と値をストアするデータ構造の追加をします。
@XmlVirtualAccessMethods
でBeanに注釈を付けて、Beanが拡張可能であることを指定し、仮想プロパティを定義します。
表6-2は、@XmlVirtualAccessMethods
注釈で使用可能な属性を説明しています。
Beanに対して、get(String)
およびset(String, Object)
メソッドを追加します。get()
メソッドは、プロパティ名によって値を返し、set()
メソッドはプロパティ名によって値をストアします。これらのメソッドのデフォルト名は、get
およびset
で、@XmlVirtualAccessMethods
注釈でオーバーライドできます。
EclipseLinkでは、ウィービングが有効な場合に、これらのメソッドがウィービングされて、遅延ロード、変更追跡、フェッチ・グループおよび内部最適化がサポートされます。
拡張された属性と値、つまり仮想マッピングをストアするデータ構造を追加します。その後、これらをデータベースにマップできます。「タスク2: 追加マッピングの提供」を参照してください。
仮想マッピングはMap
にストアするのが一般的ですが、他の方式を使用することもできます。たとえば、ディレクトリ・システムに仮想マッピングをストアすることもできます。
フィールドベースのアクセスを使用する際には、@XmlTransient
でデータ構造に注釈を付けて、この構造を別のマッピングに使用できないようにします。プロパティベースのアクセスを使用する場合、@XmlTransient
は不要です。
@XmlVirtualAccessMethods
を使用する方法に加えて、代替方法として、<access>
および<access-methods>
要素を、次のように使用することもできます。
<access>VIRTUAL</access> <access-methods set-method="get" get-method="set"/>
get
およびset
を使用する仮想アクセス・メソッドを有効にするXML:
<xml-virtual-access-methods/>
set
ではなくput
を使用する仮想アクセス・メソッドを有効にするXML(デフォルト):
<xml-virtual-access-methods set-method="put"/>
get
ではなくretrieve
を使用する仮想アクセス・メソッドを有効にするXML(デフォルト):
<xml-virtual-access-methods get-method="retrieve"/>
get
とset
ではなくretrieve
とput
を使用する仮想アクセス・メソッドを有効にするXML(デフォルト):
<xml-virtual-access-methods get-method="retrieve" set-method="put"/>
追加マッピングを提供するには、マッピングをeclipselink-oxm.xml
ファイルに追加します。次に例を示します。
<xml-element java-attribute="idNumber"/>
この項の例は、拡張可能なJAXB Beanを使用する方法を示します。この例では、他のクラスが拡張できるベース・クラスを最初に作成します。この場合、拡張可能なクラスは、Customers
およびPhoneNumbers
用です。マッピング・ファイルは2つの個別のテナントに対して作成されています。両方のテナントはいくつかの実際のプロパティを共有していますが、それぞれの要件に固有の仮想プロパティを定義します。
例6-5は、他の拡張可能なクラスが拡張できるベース・クラスExtensibleBase
を示しています。この例では、@XmlTransient
注釈を使用することにより、ExtensibleBase
が継承関係としてマップされないようにしています。モデル内で、すべてのテナントに共通な部分は、実際のプロパティで表されます。テナントごとの拡張機能は、仮想プロパティとして表されます。
例6-5 拡張可能なクラスのベース・クラス
package examples.virtual; import java.util.HashMap; import java.util.Map; import javax.xml.bind.annotation.XmlTransient; import org.eclipse.persistence.oxm.annotations.XmlVirtualAccessMethods;@XmlTransient
@XmlVirtualAccessMethods(setMethod="put")
public class ExtensibleBase { private Map<String, Object> extensions = new HashMap<String, Object>();public <T> T get(String property) {
return (T) extensions.get(property);
} public void put(String property, Object value) { extensions.put(property, value); } }
例6-6に、Customer
クラスの定義を示します。Customer
クラスは、@XmlVirtualAccessMethods
注釈が付けられたドメイン・クラスから継承するので拡張可能です。
例6-6 拡張可能なCustomerクラス
package examples.virtual;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
public class Customer extends ExtensibleBase
{
private String firstName;
private String lastName;
private Address billingAddress;
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public Address getBillingAddress() {
return billingAddress;
}
public void setBillingAddress(Address billingAddress) {
this.billingAddress = billingAddress;
}
}
例6-7に、Address
クラスを示します。モデル内のすべてのクラスを拡張可能にする必要はありません。この例のAddress
クラスには、仮想プロパティはありません。
例6-7 拡張可能ではないAddressクラス
package examples.virtual; public class Address { private String street; public String getStreet() { return street; } public void setStreet(String street) { this.street = street; } }
例6-8に、PhoneNumber
クラスを示します。Customer
と同様に、PhoneNumber
は拡張可能なクラスになります。
この項の例では、2つの個別のテナントが定義されています。両方のテナントはいくつかの実際のプロパティを共有していますが、仮想プロパティが指定されているので、対応するXML表現はまったく異なるものにできます。
テナント1
最初のテナントは、オンライン・スポーツ用品店で、次のようにモデルを拡張する必要があります。
顧客ID
顧客のミドル・ネーム
配送先住所
連絡先電話番号のコレクション
電話番号のタイプ(自宅、勤務先、または携帯など)
仮想プロパティのメタデータは、Object-XMLのXMLマッピング・ファイルを介して指定します。仮想プロパティは、実際のプロパティと同様の方法でマップされます。タイプ(リフレクションでは特定できないため)、およびコレクション・プロパティの場合はコンテナ・タイプなど、追加情報がいくらか必要です。次に定義されているCustomer
の仮想プロパティは、middleName
、shippingAddress
およびphoneNumbers
です。PhoneNumber
の仮想プロパティはtype
プロパティです。
例6-9に、binding-tenant1.xml
マッピング・ファイルを示します。
例6-9 テナント1の仮想プロパティの定義
<?xml version="1.0"?> <xml-bindings xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm" package-name="examples.virtual"> <java-types> <java-type name="Customer"> <xml-type prop-order="firstName middleName lastName billingAddress shippingAddress phoneNumbers"/> <java-attributes> <xml-attribute java-attribute="id" type="java.lang.Integer"/> <xml-element java-attribute="middleName" type="java.lang.String"/> <xml-element java-attribute="shippingAddress" type="examples.virtual.Address"/> <xml-element java-attribute="phoneNumbers" name="phoneNumber" type="examples.virtual.PhoneNumber" container-type="java.util.List"/> </java-attributes> </java-type> <java-type name="PhoneNumber"> <java-attributes> <xml-attribute java-attribute="type" type="java.lang.String"/> </java-attributes> </java-type> </java-types> </xml-bindings>
get
およびset
メソッドは、実際のプロパティを操作するためにドメイン・モデルで使用し、@XmlVirtualAccessMethods
注釈に定義されているアクセッサは、仮想プロパティを操作するために使用します。通常のJAXBメカニズムは、操作のマーシャルおよびアンマーシャルに使用します。例6-10に、仮想プロパティに関連付けられているデータを取得するための、テナント1のCustomer
クラスのコードを示します。
例6-10 仮想プロパティに関連付けられているデータを提供するためのテナント1のコード
...
Customer customer = new Customer();
//Set Customer's real properties
customer.setFirstName("Jane");
customer.setLastName("Doe");
Address billingAddress = new Address();
billingAddress.setStreet("1 Billing Street");
customer.setBillingAddress(billingAddress);
//Set Customer's virtual 'middleName' property
customer.put("middleName", "Anne");
//Set Customer's virtual 'shippingAddress' property
Address shippingAddress = new Address();
shippingAddress.setStreet("2 Shipping Road");
customer.put("shippingAddress", shippingAddress);
List<PhoneNumber> phoneNumbers = new ArrayList<PhoneNumber>();
customer.put("phoneNumbers", phoneNumbers);
PhoneNumber workPhoneNumber = new PhoneNumber();
workPhoneNumber.setNumber("555-WORK");
//Set the PhoneNumber's virtual 'type' property
workPhoneNumber.put("type", "WORK");
phoneNumbers.add(workPhoneNumber);
PhoneNumber homePhoneNumber = new PhoneNumber();
homePhoneNumber.setNumber("555-HOME");
//Set the PhoneNumber's virtual 'type' property
homePhoneNumber.put("type", "HOME");
phoneNumbers.add(homePhoneNumber);
Map<String, Object> properties = new HashMap<String, Object>();
properties.put(JAXBContextFactory.ECLIPSELINK_OXM_XML_KEY, "examples/virtual/binding-tenant1.xml");
JAXBContext jc = JAXBContext.newInstance(new Class[] {Customer.class, Address.class}, properties);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(customer, System.out);
...
例6-11に、テナント1のCustomer
クラスからのXML出力を示します。
例6-11 テナント1のCustomerクラスからのXML出力
<?xml version="1.0" encoding="UTF-8"?> <customer> <firstName>Jane</firstName> <middleName>Anne</middleName> <lastName>Doe</lastName> <billingAddress> <street>1 Billing Street</street> </billingAddress> <shippingAddress> <street>2 Shipping Road</street> </shippingAddress> <phoneNumber type="WORK">555-WORK</phoneNumber> <phoneNumber type="HOME">555-HOME</phoneNumber> </customer>
テナント2
2番目のテナントは、オンデマンドの映画や音楽を提供するストリーミング・メディア・プロバイダです。ここでは、コア・モデルに対して別のセットの拡張機能が必要になります。
1つの連絡先電話番号
このテナントの場合、実際のプロパティのマッピングのカスタマイズにもマッピング・ファイルを使用します。
例6-12に、binding-tenant2.xml
マッピング・ファイルを示します。
例6-12 テナント2の仮想プロパティの定義
<?xml version="1.0"?> <xml-bindings xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm" package-name="examples.virtual"> <xml-schema namespace="urn:tenant1" element-form-default="QUALIFIED"/> <java-types> <java-type name="Customer"> <xml-type prop-order="firstName lastName billingAddress phoneNumber"/> <java-attributes> <xml-attribute java-attribute="firstName"/> <xml-attribute java-attribute="lastName"/> <xml-element java-attribute="billingAddress" name="address"/><xml-element
java-attribute="phoneNumber"
type="examples.virtual.PhoneNumber"/>
</java-attributes> </java-type> </java-types> </xml-bindings>
例6-13に、仮想プロパティに関連付けられているデータを取得するための、テナント2のCustomer
クラスのコードを示します。
例6-13 仮想プロパティに関連付けられているデータを提供するためのテナント2のコード
...
Customer customer = new Customer();
customer.setFirstName("Jane");
customer.setLastName("Doe");
Address billingAddress = new Address();
billingAddress.setStreet("1 Billing Street");
customer.setBillingAddress(billingAddress);
PhoneNumber phoneNumber = new PhoneNumber();
phoneNumber.setNumber("555-WORK");
customer.put("phoneNumber", phoneNumber);
Map<String, Object> properties = new HashMap<String, Object>();
properties.put(JAXBContextFactory.ECLIPSELINK_OXM_XML_KEY, "examples/virtual/binding-tenant2.xml");
JAXBContext jc = JAXBContext.newInstance(new Class[] {Customer.class, Address.class}, properties);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(customer, System.out);
...
例6-14に、テナント2のCustomer
クラスからのXML出力を示します。
SaaSを実装するため重要な要素は、テナントが自分のデータのみを操作できるようにしながら、複数のアプリケーション・テナントが共有の永続性スキーマを使用できるようにすることです。単一表マルチテナントでは、すべてのアプリケーション・テナントに対して1つの表を使用し、特定のアプリケーション・コンテキスと値が設定されたテナント識別子列に基づいてアプリケーション・テナントを区別します。アプリケーションでは、必要な数の識別子列を構成することも、デフォルト動作を使用することもできます。単一表マルチテナントの詳細は、次を参照してください。
この項の内容は次のとおりです。
この項のタスクでは、SaaS環境で動作するように設計されたアプリケーションを作成する際に、単一表マルチテナントを使用する手順を説明します。
この項のタスクは次のとおりです。
単一表マルチテナントは、@Multitenant
注釈またはORM XMLファイルの<multitenant>
要素を使用するか、注釈とXMLを一緒に使用して、宣言することにより有効にできます。
@Multitenant注釈の使用
@Multitenant
注釈を使用する場合は、@Entity
または@MappedSuperclass
注釈とともにこの注釈を指定して、SINGLE_TABLE
属性を指定します。次に例を示します。
@Entity @Multitenant(SINGLE_TABLE) public class Employee { }
SINGLE_TABLE
属性は、指定されたエンティティに関連付けられている表(Table
およびSecondaryTable
)をテナント間で共有できることを示します。
<multitenant>要素の使用
<multitenant>
要素を使用する場合は、<entity>
要素内にこの要素を指定します。次に例を示します。
<entity class="model.Employee"> <multitenant type="SINGLE_TABLE"> ... </multitenant> ... </entity>
識別子列は、関連付けられているアプリケーション・コンテキストとともに使用して、表のどの行にアプリケーション・テナントがアクセスできるかを示します。複数のテナント識別子列を指定できます。テナント識別子列を指定しないと、TENANT_ID
という名前のデフォルト列が、デフォルトのeclipselink.tenant-id
コンテキスト・プロパティとともに使用されます。表またはセカンダリ表を明示的に指定しないかぎり、テナント識別子列はプライマリ表に存在すると仮定されます。デフォルト動作を変更する方法は、次を参照してください。
テナント識別子列は、@TenantDiscriminatorColumn
または@TenantDiscriminatorColumns
注釈を使用するか、ORM XMLファイルで<tenant-discriminator-column>
要素を使用して、宣言することにより指定できます。
@TenantDiscriminatorColumn注釈の使用
@TenantDiscriminatorColumn
注釈を使用する場合は、@Entity
または@MappedSuperclass
注釈とともにこの注釈を指定して、name
およびcontextProperty
属性を指定します。次に例を示します。
@Entity @Multitenant(SINGLE_TABLE) @TenantDiscriminatorColumn(name = "TENANT", contextProperty = "multi-tenant.id") public class Employee { }
複数の列を指定する場合は、複数の@TenantDiscriminatorColumn
注釈を@TenantDiscriminatorColumns
注釈内に指定し、プライマリ表に列がない場合は、列がある表を指定します。次に例を示します。
@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>要素の使用
<tenant-discriminator-column>
要素を使用する場合は、<multitenant>
要素内にこの要素を指定して、name
およびcontext-property
属性を指定します。次に例を示します。
<entity class="model.Employee"> <multitenant> <tenant-discriminator-column name="TENANT" context-property="multi-tenant.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() { ... }
オブジェクト・エンティティの一部として、識別子列が主キーにマップされるようにするには、列をマップする必要があります。次に例を示します。
@Id @Column("TENANT") public int tenant;
次の例では、ORM XMLファイルの主キーにテナント識別子列をマッピングしています。
<entity class="model.Address"> <multitenant> <tenant-discriminator-column name="TENANT" context-property="multi-tenant.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ファイルでは、次のようになります。
<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>
実行時のコンテキスト・プロパティの指定
実行時には、create entity manager factoryコールに渡される永続性ユニット定義を使用するか、個々のエンティティ・マネージャに設定して、コンテキスト・プロパティ構成を指定できます。次に例を示します。
<persistence-unit name="multi-tenant"> ... <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("multi-tenant-pu", properties).createEntityManager();
エンティティ・マネージャのプロパティ定義は、次のとおりです。
EntityManager em = Persistence.createEntityManagerFactory("multi-tenant-pu").createEntityManager(); em.beginTransaction(); em.setProperty("other.tenant.id.property", "707"); em.setProperty(EntityManagerProperties.MULTITENANT_PROPERTY_DEFAULT, "707"); ...
テナント識別子列は、エンティティ・マネージャの操作および問合せを介して、実行時に使用できます。テナント識別子列と値は、次のエンティティ・マネージャの操作でサポートされます。
persist()
find()
refresh()
テナント識別子列と値は、次のエンティティ・マネージャの問合せでサポートされます。
名前付き問合せ
すべて更新
すべて削除
注意: マルチテナントは、名前付きのネイティブ問合せではサポートされません。マルチテナント環境で名前付きのネイティブ問合せを使用する場合は、マルチテナントに関する問題を問合せで直接に手動で処理してください。一般に、マルチテナント環境では名前付きのネイティブ問合せを使用しないことをお薦めします。 |
次の参照資料を使用できます。
http://wiki.eclipse.org/EclipseLink/Examples/JPA/Multitenant
http://wiki.eclipse.org/EclipseLink/Examples/MySports
http://wiki.eclipse.org/EclipseLink/Examples/JPA/Multitenant/VPD
詳細は、『Oracle Fusion Middleware Java API Reference for EclipseLink』の次のAPIを参照してください。
org.eclipse.persistence.annotations.Multitenant
org.eclipse.persistence.annotations.TenantDiscriminatorColumn
org.eclipse.persistence.annotations.TenantDiscriminatorColumns
TopLinkでは、実行中のアプリケーションの外部にあるメタデータ・ソースにマッピング情報をストアできます。マッピング情報は、アプリケーションが永続性ユニットを作成するときに取得されるので、デプロイされたアプリケーションで動的にマッピングのオーバーライドまたは拡張を行えます。
TopLinkでは、eclipselink-orm.xml
ファイルを使用し、高度なマッピングのタイプおよびオプションをサポートできます。このファイルで、標準のJPA orm.xml
マッピング構成ファイルをオーバーライドできます。
この項には、次の項目が含まれます。
永続性ユニットを構成して、次の方法で外部メタデータを使用できます。
追加のマッピング情報が指定されたeclipselink-orm.xml
ファイルなどの外部ファイルにアクセスする最も簡単な方法は、Webサーバー上の固定URLでファイルを使用できるようにすることです。
例6-15に示すeclipselink.metadata-source.xml.url
プロパティを使用して、場所を指定します。
マルチテナント・アプリケーションでテナント固有の拡張機能を提供する場合のように、要件がさらに複雑な場合は、アプリケーション・コンテキストに基づいて外部メタデータの場所を指定できます。
例6-16に示すMetadataSource
インタフェースを実装して、場所を指定します。
例6-16 固定場所
<property name="eclipselink.metadata-source" value="mypackage.MyMetadataSource"/> <property name="eclipselink.metadata-source.xml.url" value="foo://bar"/>
例6-17に、テナントに基づいて、特定のマッピング・ファイルを返す方法を示します。
例6-17 テナント固有のマッピング・ファイル
public class AdminMetadataSource extends XMLMetadataSource { @Override public XMLEntityMappings getEntityMappings(Map<String, Object> properties, ClassLoader classLoader, SessionLog log) { String leagueId = (String) properties.get(LEAGUE_CONTEXT); properties.put(PersistenceUnitProperties.METADATA_SOURCE_XML_URL, "http://myserverlocation/rest/" + leagueId + "/orm"); return super.getEntityMappings(properties, classLoader, log); } }
メタデータ・ファイルにアクセスするには、次のいずれかの方法を使用して、マッピング・ファイルへのURLアクセスをサーバーで実現する必要があります。
静的なファイル提供サービス
自分のマッピング・ファイル、またはストアされているマッピング情報からオンデマンドで作成されるマッピング・ファイルを使用するサーバーベースのソリューション
その他のWebテクノロジ
JPAデプロイメントの詳細は、JPA仕様の次の項(http://jcp.org/en/jsr/detail?id=317
)を参照してください。
7.2項「Bootstrapping in Java SE Environments」
第7章「Container and Provider Contracts for Deployment and Bootstrapping」