データの作成・取得・削除

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()の呼び出しは同期的で、オブジェクトが保存されインデックスが更新されるまで処理が返されません。

複数のオブジェクトを保存するためには、オブジェクトのコレクションを用いてmakerPersistentAll()メソッドを呼び出します。 現在のリリースでは、これはmakePersistent()の複数回呼び出しとほとんど同じです。 将来のリリースでは、このメソッドはより効率的にデータストアへのバッチ呼び出しを実行するでしょう。

メモ:データオブジェクトの永続化フィールドが、他の永続化データオブジェクトへの参照であり、 なおかつそれらのオブジェクトが一度も保存されていないもしくは変更が加えられた場合、 それらのオブジェクトもまたデータストアに保存されます。 関係を参照ください。

キー

すべてのエンティティーは、App Engineのエンティティの中でユニークなキーをもっています。 完全なキーは幾つかの情報を持ちます。アプリケーションID、Kind、エンティティIDです。 (キーはエンティティグループの情報ももちます。 トランザクションを参照ください。)

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

アプリケーションはオブジェクト作成時にキーのIDの一部を提供することも出来ますし、データストアに数値型のキーを自動生成 させることもできます。完全なキーはデータストアのエンティティの中でユニークである必要があります。 言い換えると、オブジェクトは同じKindと同じエンティティグループの中でユニークなIDをもたなければなりません。 あなたは、フィールド型とアノテーションを使うことで、キーの振る舞いを選ぶことができます。

クラスが関係のなかで"子"として扱われる場合、キーフィールドはエンティティグループの親がどれかを 表せなければなりません。つまりそのフィールドは、KeyインスタンスかKeyを文字列にエンコードしたもののどちらかです。 エンティティグループに関しては、 トランザクションを参照ください。 関係については、 関係の項目を参照ください。

Tips:アプリケーションが別のオブジェクトと同じstring IDのオブジェクトを作成し保存した場合、オブジェクトの上書きを行います。 オブジェクト作成前にstring IDがすでに使用されているか検知するためには、トランザクションを使用し、そのIDをもつエンティティを 取得します。もしエンティティが存在しなければ、オブジェクトを作成してください。 詳しくはトランザクションを参照ください。

主キーフィールドには4種類があります。

Long

データストアにより自動的に生成されるlong整数(java.lang.Long)のID。 自動割り当てIDの親をもたないオブジェクトに使用します。 longキーフィールドは、インスタンス保存時に割り当てられます。


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

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

オブジェクト生成時にアプリケーションが割り当てる文字列型(java.lang.String)のID。 アプリケーションが割り当てるIDをもった親をもたないオブジェクトに使用します。 アプリケーションは、保存前にフィールドに好きなIDを割り当てます。


import javax.jdo.annotations.PrimaryKey;

// ...
    @PrimaryKey
    private String name;
   
Key

Keyインスタンス(com.google.appengine.api.datastore.Key)。キーの値は、(親があれば)エンティティグループの親を含み、 アプリケーションが割り当てたstring IDもしくはシステムが割り当てた数値型IDを含みます。 アプリケーション割り当てのstring IDを使用するには、Key値をIDを指定して生成し、フィールドにセットします。 システム割り当ての数値型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クラスを使ってキーを生成します。


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 as Encoded String

Keyと似ていますが、値が文字列型エンコードされています。エンコードされた文字列キーを使うことで、 アプリケーションをportable mannerに従って記述することができ、なおかつApp Engine データストアの エンティティグループを利用することもできます。


import javax.jdo.annotations.Extension;
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)
    @Extension(vendorName="datanucleus", key="gae.encoded-pk", value="true")
    private String encodedKey;
   

保存前にアプリケーションが文字列IDをセットしておいても、値をnullにしておいてもかまいません。 nullであった場合、フィールドにはシステム割り当てのキーがセットされます。

KeyインスタンスはKeyFactoryのkeyToString()やstringToKey()を用いることで、エンコードされた文字列に相互変換できます。

エンコードされたキー文字列を使用する際、追加のフィールドによって文字列IDや数値型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"フィールドにはオブジェクト保存前にキー名をセットできます。保存時に、eoncodedKeyフィールドに セットしたキー名が含められた完全なキーが割り当てられます。型はStringにします。

"gae.pk-id"フィールドはオブジェクト保存時に割り当てられ、修正できません。型はLongにします。

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

キーの生成と使用

アプリケーションが各要素のエンティティの完全なキーを知っている場合、そのオブジェクトを使用せずに、 アプリケーションは対応するKeyオブジェクトを作成することができます。

エンティティグループの親を持たないエンティティのキーは、KeyFactoryクラスのスタティックメソッドであるcreateKey()を使用できます。 このメソッドはメソッドはkind(クラスの単純名)と、アプリ割り当ての文字列型IDあるいはシステム割り当ての数値型IDを引数にし、 Keyオブジェクトを返します。 kind "Employee" の(親を持たない)エンティティの、"Alfred.Smith@example.com" という名前のキーを再現する例です。


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

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

kind "Employee"の(親を持たない)エンティティの、システム割り当ての数値型ID 52234 をもつキーを再現する例です。


        Key k = KeyFactory.createKey(Employee.class.getSimpleName(), 52234);
   

キーは、KeyFactoryクラスのkeyToString()、stringToKey()メソッドによって文字列表現に相互変換できます。 (これはKeyクラスのtoString()とは異なります。こちら側は、デバッグ用に人間が読める値に変換するメソッドです。)

エンティティグループの親をもつエンティティのキーは、KeyFactory.Builderクラスを使うことができます。


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

// ...
        Key k = new KeyFactory.Builder(Employee.class.getSimpleName(), 52234).addChild(ExpenseReport.class.getSimpleName(), "A23Z79").getKey();
   

BuilderインスタンスのaddChild()メソッドはBuilderインスタンスを返すため、キーパスの各要素を追加するために、連鎖的に呼び出しが可能です。 Builderから完全なKeyの値を得るためには、BiulderのgetKey()メソッドを呼び出します。

エンティティグループに関する詳細は、トランザクションを参照ください。

キーを使ってオブジェクトを取得する

オブジェクトを与えられたキーを使って取り出すためには、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、文字列にエンコードされたKey)をサポートし、 また、クラスのキーフィールド型と異なる型を指定することも可能です。 App Engineは、クラス名と与えられた値から、完全なキーを導き出すことが可能です。 文字列型IDと数値型IDは排他的で、数値型IDを使った呼び出しでは、文字列型IDのエンティティは決して返しません。 Key型やエンコードされたKey型が使用されている場合、キーは、必ず、kindがそれらのクラスで表されているようなエンティティを参照します。

オブジェクトを更新する

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の各フィールドに加えられた 変更を知っており、閉じられる際に自動的にデータストアを更新します。 これは、EmployeeインスタンスがPersistenceManagerに"attached"されているためです。

クラスを"detachable"として宣言することで、PersistenceManagerが閉じられた後にオブジェクトを修正できます。 これをするには、detachable属性を@PersistenceCapableアノテーションに加えます。


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

@PersistenceCapable(identityType = IdentityType.APPLICATION, detachable="true")
public class Employee {
    // ...
}
   

これで、Employeeオブジェクトを読み込んだPersistenceManagerがクローズした後でも、Employeeオブジェクトのフィールドを 読み書きできるようになりました。以下の例は、detachedオブジェクトが以下に有用か示したものです。


public Employee getEmployee(User user) {
    PersistenceManager pm = PMF.get().getPersistenceManager();
    pm.setDetachAllOnCommit(true);
    try {
        e = pm.getObjectById(Employee.class, "Alfred.Smith@example.com");
    } finally {
        pm.close();
    }
    return e;
}

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);
    }
}
   

Detechされたオブジェクトは、データトランスファーオブジェクトの良い代理役になります。 detachedオブジェクトの詳しい利用方法は、 DetaNucleusドキュメントを参照ください。

オブジェクトを削除する

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


        pm.deletePersistent(e);
   

オブジェクトが永続化された子オブジェクトを含むフィールドをもっている場合、子オブジェクトも削除されます。 詳しくは関係を参照ください。

戻る 原文