お気に入り | 日本語 | ログイン

データ クラスの定義

JDO を使用すると、普通の Java データ オブジェクト(「POJO(Plain Old Java Objects)」ともいう)をデータストアに格納できます。PersistenceManager によって永続化されたオブジェクトは、データストア内のエンティティになります。データ クラスのインスタンスの格納と再作成の方法は、Java アノテーションを使用して指定します。

注: 以前のバージョンの JDO では、Java アノテーションの代わりに .jdo XML ファイルを使用していました。これらのファイルは JDO 2.3 でも使用できますが、ここでは Java アノテーションを使用する方法のみ説明します。

クラスとフィールドのアノテーション

JDO で保存されたオブジェクトは、App Engine データストア内のエンティティになります。エンティティの種類は、クラスの単純名から派生します(内部クラスの場合は、パッケージ名なしで $ パスを使用します)。クラスの永続フィールドは、それぞれエンティティのプロパティを表します。プロパティの名前はフィールドの名前と同じになります(大文字小文字も区別されます)。

データストアでの Java クラスの格納と取得に JDO を使用できるようにするには、クラスに @PersistenceCapable アノテーションを付加します。次に例を示します:

import javax.jdo.annotations.IdentityType;
import javax.jdo.annotations.PersistenceCapable;

@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class Employee {
    // ...
}

データストアに格納するデータ クラスのフィールドは、永続フィールドとして宣言する必要があります。永続フィールドとして宣言するには、次のように @Persistent アノテーションを付加します:

import java.util.Date;
import javax.jdo.annotations.Persistent;

// ...
    @Persistent
    private Date hireDate;

永続フィールドではない(つまり、データストアに格納されず、オブジェクトを取得しても復元されない)フィールドを宣言したい場合は、フィールドに @NotPersistent アノテーションを付加します。

ヒント : JDO では、型のフィールドに @Persistent アノテーションも @NotPersistent アノテーションも付加されてい場合は、特定の型のフィールドを、デフォルトで永続フィールドとして扱います。それ以外のすべての型のフィールドは、デフォルトでは永続フィールドとしては扱われません。この動作の詳しい説明については、DataNucleus のドキュメントをご覧ください。App Engine データストアでサポートされているすべての値型が、JDO 仕様においてデフォルトで永続的と見なされるとは限らないため、フィールドには @Persistent または @NotPersistent アノテーションを明示的に付加することをおすすめします。

フィールドの型としては以下を使用できます。それぞれの型については、後ほど詳しく説明します。

  • データストアでサポートされている主な型
  • コレクション(たとえば java.util.List<...>)またはデータストアでサポートされる主な型の値の配列
  • @PersistenceCapable クラスのインスタンス、またはインスタンスのコレクション
  • Serializable クラスのインスタンス、またはインスタンスのコレクション
  • エンティティのプロパティとして格納されている埋め込みクラス

データ クラスには、対応するデータストア エンティティの主キーを格納するための専用のフィールドが 1 つ必要です。キー フィールドは 4 つの種類から選択でき、種類ごとに異なる値型とアノテーションを使って指定します(詳しくは、データの作成、取得、削除: キーをご覧ください)。最も単純なキー フィールドは長整数値です。JDO の場合は、オブジェクトを初めてデータストアに保存するときに、そのクラスのすべてのインスタンスの間で一意な値がこのキー フィールドに自動的に設定されます。長整数型のキーでは、@PrimaryKey アノテーションと、@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY) アノテーションを使用します:

ヒント: すべての永続フィールドを private または protected(あるいは package protected)にし、アクセサ メソッドによるパブリック アクセスのみを提供します。他のクラスから永続フィールドに直接アクセスすると、JDO のクラス拡張をバイパスしてしまう場合があります。他のクラスを @PersistenceAware にする方法もあります。詳しくは、DataNucleus のドキュメントをご覧ください。

import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.PrimaryKey;

// ...
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    private Long id;

次に、データ クラスの例を示します:

import java.util.Date;
import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.IdentityType;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;

@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class Employee {
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    private Long id;

    @Persistent
    private String firstName;

    @Persistent
    private String lastName;

    @Persistent
    private Date hireDate;

    public Employee(String firstName, String lastName, Date hireDate) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.hireDate = hireDate;
    }

    // Accessors for the fields.  JDO doesn't use these, but your application does.

    public Long getId() {
        return id;
    }

    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 String getHireDate() {
        return hireDate;
    } 
    public void setHireDate(Date hireDate) {
        this.hireDate = hireDate;
    } 
}

主な値型

データストアでサポートされる主な値型は以下のとおりです:

Java クラス 並び替え順序
短いテキスト文字列(500 バイト以下) java.lang.String Unicode 値の長さが 500 バイトを超えると JDOFatalUserException がスローされます。
短いバイト文字列(500 バイト以下) com.google.appengine.api.datastore.ShortBlob バイト順 値の長さが 500 バイトを超えると JDOFatalUserException がスローされます。
ブール値 boolean または java.lang.Boolean false < true
整数 shortjava.lang.Shortintjava.lang.Integerlongjava.lang.Long 数値 長整数として格納された後、フィールド型に変換されます。範囲外の値はオーバーフローします。
浮動小数点数 floatjava.lang.Floatdoublejava.lang.Double 数値 倍精度浮動小数点数として格納された後、フィールド型に変換されます。範囲外の値はオーバーフローします。
日付/時刻 java.util.Date 時系列
Google アカウント com.google.appengine.api.users.User メール アドレス(Unicode)
長いテキスト文字列 com.google.appengine.api.datastore.Text (順序付け不可) インデックス化されません。
長いバイト文字列 com.google.appengine.api.datastore.Blob (順序付け不可) インデックス化されません。
エンティティ キー com.google.appengine.api.datastore.Key、または子要素として参照されているオブジェクト パス要素単位(種類、ID、名前のいずれか、種類、ID、名前のいずれか...)
URL com.google.appengine.api.datastore.Link Unicode

注: これら以外の主な値型として、Java のデータストア API にはまだ実装されていませんが、Python API には実装されているものがあります。データストアでは、それらの値型もサポートされます。プロパティにそれらの型の値が保持されており、そのプロパティのフィールドを持つエンティティがオブジェクトに読み込まれた場合、JDO はあたかもそのプロパティが存在しないかのように動作します。つまり、そのフィールドを null に設定するか、null に設定できないフィールドの場合は NullPointerException をスローします。サポートされない型は、Category、Email、IM、PhoneNumber、PostalAddress、Rating、および GeoPt です。

主な型の単一値を保持するプロパティを表現するには、Java 型のフィールドを宣言し、@Persistent アノテーションを使用します:

import java.util.Date;
import javax.jdo.annotations.Persistent;

// ...
    @Persistent
    private Date hireDate;

シリアライズ可能なオブジェクト

フィールド値には、シリアライズ可能なクラスのインスタンスを格納できます。その場合は、インスタンスをシリアライズした値を Blob 型の単一プロパティ値として格納します。JDO で値をシリアライズするには、そのフィールドで @Persistent(serialized=true) を使用します。Blob 値はインデックス化されないため、クエリ フィルタや並び替え順序に使用することはできません。

ここでは、ファイルを表す単純なシリアライズ可能なクラスの例を示します。コンテンツ、ファイル名、および MIMI タイプが含まれています。これは JDO データ クラスではないため、永続性アノテーションは付加されていません。

import java.io.Serializable;

public class DownloadableFile implements Serializable {
    private byte[] content;
    private String filename;
    private String mimeType;

    // ... accessors ...
}

シリアライズ可能なクラスのインスタンスを Blob 値としてプロパティに格納するには、そのクラスを型とするフィールドを宣言し、@Persistent(serialized = "true") アノテーションを使用します:

import javax.jdo.annotations.Persistent;
import DownloadableFile;

// ...
    @Persistent(serialized = "true")
    private DownloadableFile file;

子オブジェクトと関係

フィールド値が @PersistenceCapable クラスのインスタンスである場合は、2 つのオブジェクト間に 1 対 1 の所有関係が作成されます。フィールドがこのような参照のコレクションである場合は、1 対多の所有関係が作成されます。

重要: 所有関係は、トランザクション、エンティティ グループ、およびカスケード削除を暗示しています。詳しくは、トランザクションおよび関係をご覧ください。

ここでは、1 対 1 の所有関係の単純な例として、Employee オブジェクトと ContactInfo オブジェクトの関係を示します:

ContactInfo.java

import com.google.appengine.api.datastore.Key;
// ... imports ...

@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class ContactInfo {
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    private Key key;

    @Persistent
    private String streetAddress;

    @Persistent
    private String city;

    @Persistent
    private String stateOrProvince;

    @Persistent
    private String zipCode;

    // ... accessors ...
}

Employee.java

import ContactInfo;
// ... imports ...

@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class Employee {
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    private Long id;

    @Persistent
    private ContactInfo myContactInfo;

    // ... accessors ...
}

この例では、アプリケーションで Employee インスタンスを生成し、その myContactInfo フィールドに新しい ContactInfo インスタンスを設定した後、pm.makePersistent(...) で Employee インスタンスを保存すると、データストアによって 2 つのエンティティが作成されます。1 つは種類が "ContactInfo" で、ContactInfo インスタンスを表します。もう 1 つは種類が "Employee" のエンティティです。ContactInfo エンティティのキーには、エンティティ グループの親として Employee エンティティのキーが含まれます。

埋め込みクラス

埋め込みクラスを使用すると、新しいデータストア エンティティを作成したり関係を形成したりしなくても、クラスを使ってフィールド値をモデル化できます。オブジェクト値のフィールドは、保持されているオブジェクトのデータストア エンティティに直接格納されます。

すべての @PersistenceCapable データ クラスは、他のデータ クラスへの埋め込みオブジェクトとして使用できます。クラスの @Persistent フィールドはオブジェクトに埋め込まれます。埋め込み対象のクラスに @EmbeddedOnly アノテーションを付加すると、埋め込みクラスとしてのみ使用できるようになります。埋め込みクラスが独立したエンティティとして格納されることはないため、主キー フィールドは必要ありません。

次に埋め込みクラスの例を示します。この例では、埋め込みクラスを、それを使用するクラスの内部クラスにしています。この方法はクラスを埋め込み可能にする上で便利ですが、必須というわけではありません。

import javax.jdo.annotations.Embedded;
import javax.jdo.annotations.EmbeddedOnly;
// ... imports ...

@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class EmployeeContacts {
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    Long id;
    
    @PersistenceCapable
    @EmbeddedOnly
    public static class ContactInfo {
        @Persistent
        private String streetAddress;

        @Persistent
        private String city;

        @Persistent
        private String stateOrProvince;

        @Persistent
        private String zipCode;

        // ... accessors ...
    }

    @Persistent
    @Embedded
    private ContactInfo homeContactInfo;
}

埋め込みクラスのフィールドは、エンティティのプロパティとして格納されます。その際には、各フィールドの名前と、それに対応するプロパティの名前が使用されます。オブジェクトに埋め込みクラス型のフィールドが複数ある場合は、フィールドの名前を変更して競合しないようにする必要があります。新しいフィールド名は、@Embedded アノテーションへの引数を使用して指定します。次に例を示します:

    @Persistent
    @Embedded
    private ContactInfo homeContactInfo;

    @Persistent
    @Embedded(members = {
        @Persistent(name="streetAddress", columns=@Column(name="workStreetAddress")),
        @Persistent(name="city", columns=@Column(name="workCity")),
        @Persistent(name="stateOrProvince", columns=@Column(name="workStateOrProvince")),
        @Persistent(name="zipCode", columns=@Column(name="workZipCode")),
    })
    private ContactInfo workContactInfo;

同様に、埋め込みオブジェクトのフィールド名が変更されていない場合は、オブジェクトのフィールド名が埋め込みクラスのフィールド名と競合しないようする必要があります。

埋め込みクラスの永続プロパティは他のフィールドと同じエンティティに格納されるため、埋め込みクラスの永続フィールドは JDOQL クエリ フィルタや並び替え順序に使用できます。埋め込みフィールドは、外部フィールドの名前、ドット(.)、および埋め込みフィールドの名前を使用して参照できます。この方法は、埋め込みフィールドのプロパティ名が @Column アノテーションによって変更されたかどうかに関係なく機能します。

    select from EmployeeContacts where workContactInfo.zipCode == "98105"

コレクション

1 つのデータストア プロパティに、複数の値を保持することもできます。JDO では、これをコレクション型の単一フィールド(主な値型またはシリアライズ可能なクラスのコレクション)で表します。以下のコレクション型がサポートされます:

  • java.util.ArrayList<...>
  • java.util.HashSet<...>
  • java.util.LinkedHashSet<...>
  • java.util.LinkedList<...>
  • java.util.List<...>
  • java.util.Set<...>
  • java.util.SortedSet<...>
  • java.util.Stack<...>
  • java.util.TreeSet<...>
  • java.util.Vector<...>

フィールドを List として宣言すると、データストアからは ArrayList 値を保持するオブジェクトが返されます。フィールドを Set として宣言すると、データストアからは HashSet が返されます。フィールドを SortedSet として宣言すると、データストアからは TreeSet が返されます。

たとえば、型が List<String> のフィールドは、そのプロパティのゼロ個以上の文字列値として、値ごとに 1 つずつ List に格納されます。

import java.util.List;
// ... imports ...

// ...
    @Persistent
    List<String> favoriteFoods;

@PersistenceCapable クラスの子オブジェクトのコレクションの場合は、1 対多関係が作成されます。詳しくは、関係をご覧ください。

複数の値を保持するデータストア プロパティは、クエリ フィルタや並び替え順序に関わる動作が特殊です。詳しくは、クエリとインデックス: 値が複数ある場合の並び替え順序とプロパティをご覧ください。

オブジェクトのフィールドとエンティティのプロパティ

App Engine データストアでは、特定のプロパティを持たないエンティティと、プロパティに null 値が設定されたエンティティが区別されます。JDO ではこの区別がサポートされておらず、null の場合も含め、オブジェクトのすべてのフィールドに値が設定されます。null に設定可能な値型(たとえば組み込み型では intboolean 以外)のフィールドを null に設定した場合は、オブジェクトを保存すると、保存後のエンティティのプロパティには null 値が設定されます。

オブジェクトに読み込まれたデータストア エンティティに、そのオブジェクトのいずれかのフィールドのプロパティがない場合、そのフィールドが null に設定可能な単一値型であれば null に設定されます。そのオブジェクトをデータストアに保存し直すと、null のプロパティはデータストアでも null 値に設定されます。フィールドが null に設定できない型の場合は、対応するプロパティがない状態でエンティティを読み込むと例外がスローされます。この例外は、インスタンスの再作成に使用したのと同じ JDO クラスからエンティティを作成した場合はスローされませんが、JDO クラスが変更された場合や、JDO ではなく低レベル API でエンティティを作成した場合はスローされます。

フィールドの型が主なデータ型またはシリアライズ可能なクラスのコレクションで、エンティティのプロパティに値がない場合、データストア内ではプロパティを単一の null 値に設定することで空のコレクションを表現します。フィールドの型が配列型である場合は、要素 0 個の配列が割り当てられます。オブジェクトが読み込まれたときにプロパティに値がない場合、フィールドには適切な型の空のコレクションが割り当てられます。データストアの内部では、空のコレクションと単一の null 値が設定されたコレクションは区別されます。

エンティティのプロパティに対応するフィールドがオブジェクトにない場合は、オブジェクトからそのプロパティにアクセスすることはできません。オブジェクトをデータストアに保存し直すと、その余分なプロパティは削除されます。

JDO では、エンティティのプロパティの型とそれに対応するオブジェクト フィールドの型が異なると、そのフィールド型への値のキャストを試みます。値をフィールド型にキャストできない場合は、ClassCastException がスローされます。数値(長整数および倍精度浮動小数点数)の場合は、キャストではなく値が変換されます。プロパティの数値がフィールド型より大きい場合、変換は例外なしでオーバーフローします。