エンティティー・スキーマの定義

ObjectGrid は、任意の数の論理エンティティー・スキーマを持つことができます。 エンティティーは、アノテーション付き Java クラス、XML、または XML と Java クラスの組み合わせを使用して定義されます。 定義されたエンティティーは、eXtreme Scale サーバーに登録され、BackingMap、索引、およびその他のプラグインにバインドされます。

エンティティー・スキーマを設計する場合は、以下のタスクを完了する必要があります。
  1. エンティティーおよびそのリレーションシップを定義します。
  2. eXtreme Scale を構成します。
  3. エンティティーを登録します。
  4. eXtreme Scale EntityManager API と対話するエンティティー・ベース・アプリケーションを作成します。

エンティティー・スキーマ構成

エンティティー・スキーマとは、1 組のエンティティーとそれらエンティティーの間のリレーションシップのことです。複数の区画を持つ eXtreme Scale アプリケーションでは、 エンティティー・スキーマには以下の制約事項およびオプションが適用されます。 エンティティーは、その初期化の前に ObjectGrid インスタンスに登録されます。 定義された各エンティティーは、固有の名前を持つ必要があり、同じ名前の ObjectGrid BackingMap に自動的にバインドされます。 初期化メソッドは、使用中の構成によって変わります。

ローカル eXtreme Scale 構成

ローカル ObjectGrid 構成を使用している場合は、エンティティー・スキーマをプログラマチックに構成できます。 このモードでは、ObjectGrid.registerEntities メソッドを使用して、アノテーション付きエンティティー・クラスまたはエンティティー・メタデータ記述子ファイルを登録することができます。

分散 eXtreme Scale 構成

分散 eXtreme Scale 構成を使用している場合は、エンティティー・スキーマを含むエンティティー・メタデータ記述子ファイルを指定する必要があります。

詳しくは、分散環境におけるエンティティー・マネージャーを参照してください。

エンティティー要件

エンティティー・メタデータは、Java クラス・ファイル、エンティティー記述子 XML ファイル、またはその両方を使用して構成します。 エンティティーに関連付ける eXtreme Scale BackingMap を識別するには、少なくとも、エンティティー記述子 XML が必要です。 アノテーション付き Java クラス (エンティティー・メタデータ・クラス) またはエンティティー記述子 XML ファイルで、 エンティティーの永続属性および他のエンティティーとのリレーションシップを記述します。 エンティティー・メタデータ・クラスを指定すると、そのクラスは、EntityManager API がグリッド内のデータと対話するためにも使用されます。

eXtreme Scale グリッドは、エンティティー・クラスを指定せずに定義できます。 サーバーとクライアントが、基盤マップに保管されたタプル・データと直接対話する場合に、これは役立つことがあります。 このようなエンティティーは、エンティティー記述子 XML ファイルで完全に定義され、クラスレス・エンティティーと呼ばれます。

クラスレス・エンティティー

クラスレス・エンティティーは、サーバーまたはクライアントのクラスパスにアプリケーション・クラスを含めることができない場合に、役立ちます。 このようなエンティティーは、エンティティー・メタデータ記述子 XML ファイルに定義されます。 このファイルで、クラスレス・エンティティー ID を使用して (形式は、「@<エンティティー ID>」)、エンティティーのクラス名を指定します。 @ 記号は、エンティティーをクラスレスとして識別し、エンティティー間の関連をマップするために使用されます。 2 つのクラスレス・エンティティーが定義されたエンティティー・メタデータ記述子 XML ファイルの例として、「クラスレス・エンティティー・メタデータ」の図を参照してください。

eXtreme Scale サーバーまたはクライアントがクラスに対するアクセス権限を備えていない場合でも、 クラスレス・エンティティーを使用して、EntityManager API を使用できます。 一般的なユース・ケースには、以下のものがあります。

  • eXtreme Scale コンテナーが、クラスパス内のアプリケーション・クラスを許可しないサーバーにホストされている。 この場合でも、クライアントは、クラスが許可されているクライアントから EntityManager API を使用して、グリッドにアクセスできます。
  • eXtreme Scale クライアントが非 Java クライアント (eXtreme Scale REST データ・サービス) を使用しているか、ObjectMap API を使用してグリッド内のタプル・データにアクセスしているため、 クライアントには、エンティティー・クラスに対するアクセス権限が必要ない。

クライアントとサーバー間でエンティティー・メタデータに互換性がある場合には、 エンティティー・メタデータ・クラス、XML ファイル、またはその両方を使用して、エンティティー・メタデータを作成できます。

例えば、下図の「プログラマチック・エンティティー・クラス」は、次のセクションのクラスレス・メタデータ・コードと互換性があります。

プログラマチック・エンティティー・クラス
@Entity
public class Employee {
    @Id long serialNumber;
    @Basic byte[] picture;
    @Version int ver;
    @ManyToOne(fetch=FetchType.EAGER, cascade=CascadeType.PERSIST)
    Department department;
}

@Entity
public static class Department {
    @Id int number;
    @Basic String name;
    @OneToMany(fetch=FetchType.LAZY, cascade=CascadeType.ALL, mappedBy="department")
    Collection<Employee> employees;
}

クラスレス・フィールド、キー、およびバージョン

前述のとおり、クラスレス・エンティティーは、エンティティー XML 記述子ファイルで完全に構成されます。 クラス・ベースのエンティティーは、Java フィールド、プロパティー、およびアノテーションを使用して属性を定義します。 そのため、クラスレス・エンティティーは、<basic> タグおよび <id> タグを使用して、 エンティティー XML 記述子でキーおよび属性構造を定義する必要があります。

クラスレス・エンティティー・メタデータ
<?xml version="1.0" encoding="UTF-8"?>
<entity-mappings xmlns="http://ibm.com/ws/projector/config/emd"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://ibm.com/ws/projector/config/emd ./emd.xsd">

<entity class-name="@Employee" name="Employee">
    <attributes>
        <id name="serialNumber" type="long"/>
        <basic name="firstName" type="java.lang.String"/>
        <basic name="picture" type="[B"/>
        <version name="ver" type="int"/>
        <many-to-one 
            name="department" 
            target-entity="@Department"
            fetch="EAGER"">
                <cascade><cascade-persist/></cascade>
        </many-to-one>
    </attributes>
</entity>

<entity class-name="@Department" name="Department" >
    <attributes>
        <id name="number" type="int"/>
        <basic name="name" type="java.lang.String"/>
        <version name="ver" type="int"/>
        <one-to-many 
            name="employees" 
            target-entity="@Employee" 
            fetch="LAZY" 
            mapped-by="department">
            <cascade><cascade-all/></cascade>
        </one-to-many>
    </attributes>
</entity>

上記の各エンティティーに <id> エレメントが含まれていることに注意してください。 クラスレス・エンティティーには、1 つ以上の <id> エレメントを定義するか、エンティティーのキーを表す単一値のアソシエーションを指定する必要があります。 エンティティーのフィールドは、<basic> エレメントによって表されます。 クラスレス・エンティティーでは、<id>、<version>、および <basic> の各エレメントには名前および型が必要です。 サポートされる型の詳細については、以下のサポートされる属性型のセクションを参照してください。

エンティティー・クラスの要件

クラス・ベースのエンティティーは、さまざまなメタデータを Java クラスに関連付けることによって識別されます。 メタデータは、Java Platform, Standard Edition 5 のアノテーション、エンティティー・メタデータ記述子ファイル、またはアノテーションと記述子ファイルの組み合わせを使用して指定できます。エンティティー・クラスは、以下の基準を満たしている必要があります。 すべてのエンティティーは固有の名前と型を持っています。アノテーションを使用している場合、名前はデフォルトではクラスの単純名 (短い名前) ですが、@Entity アノテーションの name 属性を使用してオーバーライドできます。

パーシスタント属性

エンティティーのパーシスタント状態は、フィールド (インスタンス変数) を使用するか、Enterprise JavaBeans スタイルのプロパティー・アクセサーを使用して、クライアントおよびエンティティー・マネージャーによってアクセスされます。各エンティティーは、フィールドまたはプロパティー・ベースのいずれかのアクセスを定義する必要があります。 アノテーション付きエンティティーは、クラス・フィールドがアノテーション付きの場合はフィールド・アクセスとなり、プロパティーの getter メソッドがアノテーション付きである場合はプロパティー・アクセスとなります。フィールド・アクセスとプロパティー・アクセスを混在させることはできません。タイプを自動的に判別できない場合は、@Entity アノテーションまたは同等の XML で accessType 属性を使用してアクセス・タイプを識別できます。

パーシスタント・フィールド
フィールド・アクセス・エンティティー・インスタンス変数は、エンティティー・マネージャーおよびクライアントから直接アクセスされます。transient 修飾子または transient アノテーションでマークされているフィールドは無視されます。パーシスタント・フィールドの修飾子を final または static にすることはできません。
パーシスタント・プロパティー
プロパティー・アクセス・エンティティーは、読み取りおよび書き込みプロパティーに関しては、JavaBeans シグニチャー規則に従う必要があります。 JavaBeans 規則に従わないメソッド、または getter メソッドに Transient アノテーションを持つメソッドは無視されます。 型 T のプロパティーの場合、型 T の値を返す getProperty という getter メソッドと、 setProperty(T) という void setter メソッドが必要です。 ブール型の場合、getter メソッドは、true または false を返す isProperty と表すことができます。 パーシスタント・プロパティーは、static 修飾子を持つことができません。
サポートされる属性タイプ
以下のパーシスタント・フィールドおよびプロパティー・タイプがサポートされます。
  • ラッパーを含む Java プリミティブ型
  • java.lang.String
  • java.math.BigInteger
  • java.math.BigDecimal
  • java.util.Date
  • java.util.Calendar
  • java.sql.Date
  • java.sql.Time
  • java.sql.Timestamp
  • byte[]
  • java.lang.Byte[]
  • char[]
  • java.lang.Character[]
  • enum
ユーザー・シリアライズ可能属性の型はサポートされてはいますが、パフォーマンス、照会、および変更検出に制約があります。配列やユーザー・シリアライズ可能オブジェクトなど、プロキシー処理できないパーシスタント・データは、変更された場合にはエンティティーに再割り当てする必要があります。

シリアライズ可能な属性は、 エンティティー記述子 XML ファイルで、オブジェクトのクラス名を使用して表されます。 オブジェクトが配列である場合には、データ型は Java 内部形式を使用して表されます。 例えば、属性データ型が java.lang.Byte[][] である場合には、ストリング表現は [[Ljava.lang.Byte; になります。

ユーザー・シリアライズ可能型は、以下のベスト・プラクティスに従っている必要があります。

  • ハイパフォーマンス・シリアライゼーション・メソッドを実装します。 java.lang.Cloneable インターフェースおよび public clone メソッドを実装します。
  • java.io.Externalizable インターフェースを実装します。
  • equals および hashCode を実装します。

エンティティー・アソシエーション

双方向および単一方向のエンティティー・アソシエーションまたはエンティティー間リレーションシップは、1 対 1、多対 1、1 対多、および多対多として定義できます。エンティティー・マネージャーは、エンティティーを保管するときに、自動的にエンティティー・リレーションシップを適切なキー参照に解決します。

eXtreme Scale グリッドはデータ・キャッシュであり、データベースとは異なり、参照保全性を実施しません。 リレーションシップでは子エンティティーに対して永続化操作および除去操作をカスケードすることができますが、オブジェクトとのリンク切れを検出または引き起こすことはありません。子オブジェクトを除去する場合は、そのオブジェクトへの参照を親から除去する必要があります。

2 つのエンティティー間の双方向アソシエーションを定義する場合、リレーションシップの所有者を識別する必要があります。対多アソシエーションでは、 リレーションシップの多側は常に所有側になります。 所有権を自動的に判別できない場合、アノテーションの mappedBy 属性、または XML におけるそれと同等な属性を指定する必要があります。mappedBy 属性は、リレーションシップの所有者であるターゲット・エンティティー内のフィールドを識別します。この属性は、型と基数が同じである複数の属性が存在する場合に、関連するフィールドを識別するのにも役立ちます。

単一値アソシエーション

1 対 1 および多対 1 のアソシエーションは、@OneToOne および @ManyToOne アノテーションまたはそれと等価な XML 属性を使用して示されます。ターゲット・エンティティーの型は、属性の型によって決定されます。以下の例では、Person と Address の間に単一方向アソシエーションを定義しています。 Customer エンティティーは、1 つの Address エンティティーへの参照を持っています。この場合、逆のリレーションシップがないため、アソシエーションを多対 1 にすることもできます。
@Entity
public class Customer {
  @Id id;
  @OneToOne Address homeAddress;
}

@Entity
public class Address{
  @Id id
  @Basic String city;
}
Customer クラスと Address クラスの間の双方向リレーションシップを指定するには、 Address クラスから Customer クラスへの参照を追加し、適切なアノテーションを追加して、リレーションシップの反対側にマークを付けます。 このアソシエーションは 1 対 1 であるため、@OneToOne アノテーションで mappedBy 属性を使用してリレーションシップの所有者を指定する必要があります。
@Entity
public class Address{
  @Id id
  @Basic String city;
  @OneToOne(mappedBy="homeAddress") Customer customer;
}

コレクション値アソシエーション

1 対多および多対多のアソシエーションは、@OneToMany および @ManyToMany アノテーションまたはそれと等価な XML 属性を使用して示されます。多くのリレーションシップはすべて、java.util.Collection、java.util.List、または java.util.Set という型を使用して表されます。 ターゲット・エンティティーの型は、Collection、List、または Set という汎用型によって決定されるか、@OneToMany または @ManyToMany アノテーションの targetEntity 属性 (またはそれと等価な XML での属性) を使用して明示的に決定されます。
前出の例では、顧客ごとに 1 つの住所オブジェクトを持たせることは現実的ではありません。それは、多くの顧客が 1 つの住所を共有したり、複数の住所を持っていることがあるからです。これを解決する 1 つの良い方法は、「多」のアソシエーションを使用することです。
@Entity
public class Customer {
  @Id id;
  @ManyToOne Address homeAddress;
  @ManyToOne Address workAddress;
}

@Entity
public class Address{
  @Id id
  @Basic String city;
  @OneToMany(mappedBy="homeAddress") Collection<Customer> homeCustomers;


  @OneToMany(mappedBy="workAddress", targetEntity=Customer.class) 
		Collection workCustomers;
}
この例では、同じエンティティー間に「自宅アドレス」リレーションシップと「勤務先アドレス」リレーションシップという 2 つの異なるリレーションシップが存在します。workCustomers 属性に非汎用型の Collection が使用されているのは、 汎用型を使用できない場合に targetEntity 属性を使用する方法を示すためです。

クラスレス・アソシエーション

クラスレス・エンティティー・アソシエーションは、クラス・ベース・アソシエーションの定義方法と同じようなエンティティー・メタデータ記述子 XML ファイルで定義されます。 唯一の違いは、ターゲット・エンティティーが実際のクラスを指すのではなく、 エンティティーのクラス名で使用されるクラスレス・エンティティー ID を指すという点です。

次に例を挙げます。

<many-to-one name="department" target-entity="@Department" fetch="EAGER">
      <cascade><cascade-all/></cascade>
</many-to-one>
<one-to-many name="employees" target-entity="@Employee" fetch="LAZY">
      <cascade><cascade-all/></cascade>
</one-to-many>

1 次キー

すべてのエンティティーは 1 次キーを持つ必要があり、このキーは単純キー (単一属性) または複合キー (複数属性) として指定できます。 キー属性は Id アノテーションを使用して示すか、またはエンティティー XML 記述子ファイルで定義します。キー属性には以下の要件があります。 複合 1 次キーは、必要に応じて 1 次キー・クラスを定義できます。エンティティーは、@IdClass アノテーションまたはエンティティー XML 記述子ファイルを使用して、1 次キー・クラスに関連付けられます。 @IdClass アノテーションは、EntityManager.find メソッドと一緒に使用する場合に役立ちます。
1 次キー・クラスには以下の要件があります。
  • 引数なしのコンストラクターを使用した public である必要があります。
  • 1 次キー・クラスのアクセス・タイプは、1 次キー・クラスを宣言しているエンティティーによって決定されます。
  • プロパティー・アクセスの場合、1 次キー・クラスのプロパティーは、public または protected にする必要があります。
  • 1 次キーのフィールドまたはプロパティーは、参照側のエンティティーで定義されているキー属性の名前と型に一致している必要があります。
  • 1 次キー・クラスは、equals メソッドおよび hashCode メソッドを実装している必要があります。
次に例を挙げます。
@Entity
@IdClass(CustomerKey.class)
public class Customer {
    @Id @ManyToOne Zone zone;
    @Id int custId;
    String name;
    ...
}

@Entity
public class Zone{
    @Id String zoneCode;
    String name;
}

public class CustomerKey {
    Zone zone;
    int custId;

    public int hashCode() {...}
    public boolean equals(Object o) {...}
}

クラスレス 1 次キー

クラスレス・エンティティーは、XML ファイルで属性 id=true を指定した、少なくとも 1 つの <id> エレメントまたはアソシエーションを持つ必要があります。 両方の例は、以下のようになります。

<id name="serialNumber" type="int"/>
<many-to-one name="department" target-entity="@Department" id="true">
<cascade><cascade-all/></cascade>
</many-to-one>
要確認:
クラスレス・エンティティーでは、<id-class> XML タグはサポートされません。

エンティティー・プロキシーおよびフィールド・インターセプト

エンティティー・クラスおよび可変のサポートされる属性型は、 プロパティー・アクセス・エンティティーではプロキシー・クラスによって拡張され、 Java Development Kit (JDK) 5 のフィールド・アクセス・エンティティーではバイト・コード拡張されています。 内部ビジネス・メソッドおよび equals メソッドを使用する場合であっても、エンティティーにアクセスする場合は常に、適切なフィールド・アクセス・メソッドまたはプロパティー・アクセス・メソッドを使用する必要があります。

プロキシーおよびフィールド・インターセプターを使用すると、エンティティー・マネージャーがエンティティーの状態を追跡して、エンティティーが変更されたかどうかを判別し、パフォーマンスを改善できるようになります。フィールド・インターセプターは、エンティティー・インスツルメンテーション・エージェントの構成時に、Java SE 5 プラットフォームでのみ使用できます。

重要: プロパティー・アクセス・エンティティーを使用している場合、equals メソッドによる現行のインスタンスと入力オブジェクトの比較には instanceof 演算子を使用する必要があります。ターゲット・オブジェクトのすべてのイントロスペクションは、 フィールド自体ではなくオブジェクトのプロパティーを介して行う必要があります。これは、オブジェクト・インスタンスがプロキシーになるからです。