JDO でのデータの作成、取得、削除

データストアへの JDO データ オブジェクトの保存は、PersistenceManager インスタンスの makePersistent() メソッドを呼び出して簡単に行えます。App Engine の JDO 実装では、オブジェクトの主キーフィールドを使用して、どのデータストア エンティティがデータ オブジェクトに対応するかを管理できます。また、新しいオブジェクトのキーを自動的に生成することもできます。キーを使用するとエンティティをすばやく取得できます。キーは、既知の値(アカウント ID など)から生成できます。

オブジェクトの永続化

データストアにシンプルなデータ オブジェクトを格納するには、PersistenceManager の makePersistent() メソッドを呼び出して、そのメソッドにインスタンスを渡します。

PersistenceManager pm = PMF.get().getPersistenceManager();

Employee e = new Employee("Alfred", "Smith", new Date());

try {
    pm.makePersistent(e);
} finally {
    pm.close();
}

makePersistent() の呼び出しは同期的に処理されるため、オブジェクトが保存されてインデックスが更新されるまでは戻りません。

複数の JDO オブジェクトを保存するには、オブジェクトのコレクションを指定して makePersistentAll(...) メソッドを呼び出します。このメソッドは、現在は一連の makePersistent(...) 呼び出しのような形で実装されていますが、将来的にはより効率的な単一のバッチ保存オペレーションとして実装される予定です。

注: データ オブジェクトの永続化フィールドのいずれかが他の永続化データ オブジェクトの参照で、参照先のオブジェクトが一度も保存されたことがない、または読み込み後に変更されている場合は、参照先のオブジェクトもデータストアに保存されます。詳細については関係をご覧ください。

キー

すべてのエンティティに、App Engine のエンティティ全体で一意のキーがあります。完全なキーには、アプリケーション ID、種類、エンティティ ID など、複数の情報が含まれます(キーには、エンティティ グループに関する情報も含まれます。詳細については、 トランザクションをご覧ください)。

オブジェクトのキーは、インスタンスのフィールドに格納されます。主キーフィールドは、@PrimaryKey アノテーションで指定します。

キーの ID 部分は、アプリでオブジェクトを作成するときに文字列として指定するか、データストアで数値の ID を自動的に生成します。完全なキーは、データストアのエンティティ全体で一意である必要があります。つまり、オブジェクトには、同じ種類および同じエンティティ グループの親(ある場合)のオブジェクト全体で一意の ID が設定されていなければなりません。キーの動作は、フィールドの型とアノテーションを使用して選択します。

関係の「子」クラスとなるクラスの場合、キーフィールドは、エンティティ グループの親を表すことができる型、つまり、Key インスタンス、または文字列としてエンコードされた Key 値のいずれかにします。エンティティ グループの詳細についてはトランザクションを、関係の詳細については関係をご覧ください。

ヒント: アプリで新しく作成されたオブジェクトの文字列 ID が、同じ種類(および同じエンティティ グループの親)の別のオブジェクトの ID と重複する場合、新しく作成したオブジェクトを保存すると、データストア内の既存のオブジェクトが上書きされます。新しいオブジェクトを作成する前に、文字列 ID が既に使用されているかどうかを確かめるには、トランザクションを使用して、その ID を持つエンティティの取得を試みます。そして、該当するエンティティが存在しない場合に、オブジェクトを作成します。詳細については、トランザクションをご覧ください。

主キー フィールドには次の 4 つの型があります。

Long 型

長整数(java.lang.Long)。データストアによって自動的に生成されるエンティティ ID。データストアによって ID が自動生成され、エンティティ グループの親を持たないオブジェクトに対して使用します。インスタンスの長整数型キー フィールドは、インスタンスの保存時に設定されます。

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

// ...
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    private Long id;
エンコードされていない文字列

文字列(java.lang.String)。オブジェクトの作成時にアプリケーションによって提供されるエンティティ ID(「キー名」)。アプリケーションによって ID が提供され、エンティティ グループの親を持たないオブジェクトに対して使用します。エンティティの保存前に、アプリケーションでこのフィールドに任意の ID を設定します。

import javax.jdo.annotations.PrimaryKey;

// ...
    @PrimaryKey
    private String name;
キー

キー インスタンス(com.google.appengine.api.datastore.Key)。キー値は、エンティティ グループの親のキー(ある場合)に加え、アプリによって割り当てられる文字列 ID またはシステムによって生成される数値 ID のいずれかで構成されます。アプリによって割り当てられる文字列 ID を使用してオブジェクトを作成するには、その ID で Key 値を作成し、フィールドにその値を設定します。システムによって割り当てられる数値 ID を使用してオブジェクトを作成するには、キーフィールドを null のままにします(エンティティ グループの親を使用する方法については、トランザクションをご覧ください)。

import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;
import com.google.appengine.api.datastore.Key;

// ...
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    private Key key;

    public void setKey(Key key) {
        this.key = key;
    }

次のように KeyFactory クラスを使用して、アプリケーションで Key インスタンスを作成できます。

import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;

// ...
        Key key = KeyFactory.createKey(Employee.class.getSimpleName(), "Alfred.Smith@example.com");
        Employee e = new Employee();
        e.setKey(key);
        pm.makePersistent(e);
エンコードされた文字列としてのキー

Key と似ていますが、値はエンコードされた文字列形式のキーです。エンコードされた文字列のキーを使用すると、App Engine データストアのエンティティ グループを利用しながら、移植しやすいアプリケーションを開発できます。

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

// ...
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    @Extension(vendorName="datanucleus", key="gae.encoded-pk", value="true")
    private String encodedKey;

アプリケーションでは、名前が指定されたキーを使用して保存前にこの値を設定することも、null のままにすることもできます。エンコードされたキーのフィールドが null の場合、オブジェクトの保存時に、システムによって生成されたキーがこのフィールドに設定されます。

Key インスタンスとエンコードされた文字列表現は、KeyFactory の keyToString() メソッドと stringToKey() メソッドを使用して相互に変換できます。

エンコードされたキー文字列を使用している場合は、フィールドを追加して、オブジェクトの文字列または数値 ID へのアクセスを提供できます。

    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    @Extension(vendorName="datanucleus", key="gae.encoded-pk", value="true")
    private String encodedKey;

    @Persistent
    @Extension(vendorName="datanucleus", key="gae.pk-name", value="true")
    private String keyName;

    // OR:

    @Persistent
    @Extension(vendorName="datanucleus", key="gae.pk-id", value="true")
    private Long keyId;

"gae.pk-name" フィールドは、オブジェクトを保存する前にキー名に設定できます。オブジェクトの保存時に、そのキー名を含む完全なキーが、エンコードされたキーフィールドに設定されます。型は String である必要があります。

"gae.pk-id" フィールドはオブジェクトの保存時に設定され、変更できません。型は Long である必要があります。

生成されたキー(valueStrategy = IdGeneratorStrategy.IDENTITY を使用したキーフィールド)を使用して新しいオブジェクトを作成する場合、最初のキー値は null になります。キーフィールドは、オブジェクトがデータストアに書き込まれるときに設定されます。トランザクションを使用している場合、オブジェクトは、トランザクションのコミット時に書き込まれます。トランザクションを使用していない場合、オブジェクトは makePersistent() メソッドが呼び出されたとき(オブジェクトが作成中の場合)、または PersistenceManager インスタンスの close() メソッドが呼び出されたとき(オブジェクトが更新中の場合)に書き込まれます。

キーの作成の詳細については、エンティティ、プロパティ、キーをご覧ください。

キーによるオブジェクトの取得

キーを指定してオブジェクトを取得するには、PersistenceManager の getObjectById() メソッドを使用します。このメソッドはオブジェクトのクラスとキーを受け取ります。

        Key k = KeyFactory.createKey(Employee.class.getSimpleName(), "Alfred.Smith@example.com");
        Employee e = pm.getObjectById(Employee.class, k);

クラスで使用されるキーフィールドが、エンコードされていない文字列 ID(String)または数値 ID(Long)である場合、getObjectByID() は、単純な値をキーパラメータとして受け取ることができます。

        Employee e = pm.getObjectById(Employee.class, "Alfred.Smith@example.com");

キーの引数には、サポートされているキーフィールドの型(文字列 ID、数値 ID、Key 値、エンコードされたキー文字列)を指定できます。この引数の型は、クラスのキーフィールドと違っていても構いません。クラス名と指定された値に基づいて、App Engine で完全なキーを生成できる必要があります。文字列 ID と数値 ID は排他的です。したがって、数値 ID による呼び出しで文字列 ID のエンティティが返されることはありません。Key 値またはエンコードされたキー文字列を使用している場合、キーの参照先エンティティの種類をクラスで表す必要があります。

オブジェクトの更新

JDO でオブジェクトを更新する 1 つの方法として、オブジェクトをフェッチし、そのオブジェクトを返した PersistenceManager がまだ開いているうちに、オブジェクトを変更するという方法があります。 この場合、PersistenceManager が閉じると、変更が永続化されます。例:

public void updateEmployeeTitle(User user, String newTitle) {
    PersistenceManager pm = PMF.get().getPersistenceManager();
    try {
        Employee e = pm.getObjectById(Employee.class, user.getEmail());
        if (titleChangeIsAuthorized(e, newTitle) {
            e.setTitle(newTitle);
        } else {
            throw new UnauthorizedTitleChangeException(e, newTitle);
        }
    } finally {
        pm.close();
    }
}

Employee インスタンスは PersistenceManager によって返されたものなので、PersistenceManager では、Employee の Persistent フィールドに対して行われたすべての変更を認識します。この変更は、PersistenceManager が閉じるときにデータストアに自動的に反映されます。これは、Employee インスタンスが PersistenceManager に「アタッチ」されているからです。

PersistenceManager が閉じた後でオブジェクトを変更するには、クラスを「デタッチ可能」として宣言します。そのためには、detachable 属性を @PersistenceCapable アノテーションに追加します。

import javax.jdo.annotations.PersistenceCapable;

@PersistenceCapable(detachable="true")
public class Employee {
    // ...
}

これで、Employee オブジェクトを読み込んだ PersistenceManager が閉じ、そのオブジェクトのフィールドを読み書きできるようになりました。次の例は、デタッチしたオブジェクトがいかに便利かを示しています。

public Employee getEmployee(User user) {
    PersistenceManager pm = PMF.get().getPersistenceManager();
    Employee employee, detached = null;
    try {
        employee = pm.getObjectById(Employee.class,
            "Alfred.Smith@example.com");

        // If you're using transactions, you can call
        // pm.setDetachAllOnCommit(true) before committing to automatically
        // detach all objects without calls to detachCopy or detachCopyAll.
        detached = pm.detachCopy(employee);
    } finally {
        pm.close();
    }
    return detached;
}

public void updateEmployeeTitle(Employee e, String newTitle) {
    if (titleChangeIsAuthorized(e, newTitle) {
        e.setTitle(newTitle);
        PersistenceManager pm = PMF.get().getPersistenceManager();
        try {
            pm.makePersistent(e);
        } finally {
            pm.close();
        }
    } else {
        throw new UnauthorizedTitleChangeException(e, newTitle);
    }
}

デタッチしたオブジェクトは、データ移転オブジェクトを作成する代わりとして使用できます。デタッチしたオブジェクトの操作の詳細については、DataNucleus のドキュメントをご覧ください。

オブジェクトの削除

データストアからオブジェクトを削除するには、オブジェクトを指定して PersistenceManager の deletePersistent() メソッドを呼び出します。

pm.deletePersistent(e);

JDO で複数のオブジェクトを削除するには、オブジェクトのコレクションを指定して deletePersistentAll(...) メソッドを呼び出します。このメソッドは、現在は一連の deletePersistent(...) 呼び出しのような形で実装されていますが、将来的にはより効率的な単一の一括削除オペレーションとして実装される予定です。

オブジェクトのフィールドに永続的な子オブジェクトが含まれる場合は、その子オブジェクトも削除されます。詳細については、関係をご覧ください。

クエリに合致するすべてのオブジェクトを削除するには、JDOQL の「クエリによる削除」機能を使用します。詳細については、クエリによるエンティティの削除をご覧ください。