データストアのローレベル API と JDO

この資料では、ローレベル・データストア API を利用して Google App Engine のデータストアにエンティティを保存する方法を説明します。 また、JDO を利用したハイレベルな API を利用する方法をおさらいして、ローレベルな Entity と JDO データクラスの違い、共通点について説明します。

Google App Engine のデータストアサービスでのデータ保存単位は Entity

Google App Engine のデータストア (DataStore) に保存するデータの基本単位はエンティティ (Entity) といいます。 クラスは com.google.appengine.api.datastore.Entity です。

エンティティはキー (Key, com.google.appengine.api.datastore.Key) で一意に識別されます。エンティティをデータストアに保存したところで、キーが割り当てられます。

データストアへアクセスするにはデータストアサービス (DatastoreService, com.google.appengine.api.datastore.DatastoreService) を通してアクセスします。 データストアサービスは、データストアサービスファクトリのスタティックメソッドである getDatastoreService で取得できます。

DatastoreService datastoreService = DatastoreServiceFactory.getDatastoreService();

エンティティは種類 (kind) で区別されます。例えば、Employee という種類 (カインド, Kind) の Entity オブジェクトを作るなら、次のようにします。

Entity entity = new Entity("Employee");

また親の Entity を指定できるなどの、親子構造も作れるのですがここではそれは説明しません。

Entity の保存

実際に Employee というカインドの Entity を保存するコード見てみましょう。

import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.Key;

//...

DatastoreService datastoreService = DatastoreServiceFactory.getDatastoreService();
Entity entity = new Entity("Employee");
Key key = datastoreService.put(entity);
         
System.out.println(key.toString());

コードの最後で Key を出力していますが、これを実行した結果、次のようになります。

Employee(1)

ちなみに、Eclipse では System.out.println で出力したものは、Console に出力されます。

エンティティの取得

エンティティを取得するのも簡単です。やはり データストアサービスを通して行います。

import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.PreparedQuery;
import com.google.appengine.api.datastore.Query;

//...

DatastoreService datastoreService
     = DatastoreServiceFactory.getDatastoreService();
Query query = new Query("Employee");
PreparedQuery preparedQuery = datastoreService.prepare(query);
for(Entity empEntity : preparedQuery.asIterable()){
     System.out.println(
          empEntity.getKind() + " - " + empEntity.getKey() );
}

ここでは Employee カインドのエンティティを全て取得しています。クエリーの詳細については別の資料で説明します。

エンティティの削除

エンティティの削除は delete メソッドで行います。

import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.PreparedQuery;
import com.google.appengine.api.datastore.Query;

//...
DatastoreService datastoreService
     = DatastoreServiceFactory.getDatastoreService();
Query query = new Query("Employee");
         
PreparedQuery preparedQuery = datastoreService.prepare(query);
for(Entity empEntity : preparedQuery.asIterable()){
     datastoreService.delete(empEntity.getKey());
}

以上、エンティティの保存、取得、削除という基本をみました。

Entity へのプロパティの設定

上では単純にエンティティを保存しただけでしたが、実際にはエンティティには、値を設定して保存します。 そうじゃないと、データを保存する意味がないですよね?

エンティティに値を設定するときは、名前と値のペアとなるプロパティをセットすることで追加の値を保存できます

プロパティの設定は setProperty( String name, Object value) メソッド。プロパティ値の取得には Object getProperty(String name) メソッドを用います。 設定、取得ともに値は Object として設定できます。

ちなみに、プロパティの値は基本的にインデックスされます。しかし、BlobやTextはインデックスされません。その他のインデックスされない型を保存するには、setUnindexedProperty メソッドを使います。

エンティティへのプロパティの設定と保存

それでは、上でみた Employee エンティティを保存するときに、プロパティを設定して保存する方法をみてみましょう。

ここでは EmployeeID、FirstName、LastName という三つのプロパティに値を設定して保存します。

import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.Key;

//...

DatastoreService datastoreService = DatastoreServiceFactory.getDatastoreService();
Entity entity = new Entity("Employee");
entity.setProperty("employeeID", "12345");
entity.setProperty("firstName", "Keisuke");
entity.setProperty("lastName", "Oyama");
    
Key key = datastoreService.put(entity);

プロパティに設定された値によるフィルター・クエリー

エンティティを取得するときに、プロパティの値でフィルターを行うことができます。 例えば、lastName プロパティの値が "Oyama" という値をもつエンティティをデータストアから取得するコードは次のようになります。

import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.PreparedQuery;
import com.google.appengine.api.datastore.Query;

//...

DatastoreService datastoreService
     = DatastoreServiceFactory.getDatastoreService();
Query query = new Query("Employee");
query.addFilter("lastName", Query.FilterOperator.EQUAL, "Oyama");

PreparedQuery preparedQuery = datastoreService.prepare(query);
for(Entity empEntity : preparedQuery.asIterable()){
     System.out.println(
          empEntity.getKind() + " - " + empEntity.getKey()
     );
}

データストアのローレベルの動きは以上のとおりです。 では次に JDO を用いてどのようにデータクラスを定義するかみてみましょう。

JDO で Entity となるデータクラスを 定義する

JDO で Entity となる「データクラス」を定義できます。

データクラスを定義するには、クラス定義に @PersistenceCapable を指定して、保存するフィールドには @Persistent を指定します。

例えば、上記のように、employeeID、firstName、lastName というプロパティを持つ Employee カインドのエンティティは、 次のようなフィールドを持つ Employee クラスとして定義できます。

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

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

@PersistenceCapable
public class Employee {

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

     @Persistent
     private String employeeID;

     @Persistent
     private String firstName;

     @Persistent
     private String lastName;

     public Employee(
          String EmployeeID,
          String FirstName,
          String LastName
     ) {
          this.employeeID = EmployeeID;
          this.firstName = FirstName;
          this.lastName = LastName;
     }

     public Key getKey(){
          return key;
     }

     public String getEmployeeID(){
          return employeeID;
     }

     public String getFirstName(){
          return firstName;
     }

     public String getLastName(){
          return lastName;
     }

}

データオブジェクトの保存

JDO データオブジェクトを保存するコードは次のとおりです。

import javax.jdo.JDOHelper;
import javax.jdo.PersistenceManager;
import javax.jdo.PersistenceManagerFactory;
import javax.servlet.http.*;

//...
          
Employee emp = new Employee(
     "11111", 
     "Grace",
     "Oyama");
                    
PersistenceManagerFactory pmf 
     = JDOHelper.getPersistenceManagerFactory("transactions-optional");
PersistenceManager pm = pmf.getPersistenceManager();
          
try{
     emp = pm.makePersistent(emp);
}
finally{
     pm.close();
}

JDO データオブジェクトの取得 ~ 生エンティティと同様

こうして保存されたデータオブジェクトは、先に見た Entity と同様にデータストアから取得可能です。

DatastoreService datastoreService 
     = DatastoreServiceFactory.getDatastoreService();
Query query = new Query("Employee");
query.addFilter("lastName", Query.FilterOperator.EQUAL, "Oyama");

PreparedQuery preparedQuery = datastoreService.prepare(query);
for(Entity empEntity : preparedQuery.asIterable()){
     System.out.println(
          empEntity.getKind() 
          + " - " + empEntity.getKey()
          + " - " + empEntity.getProperty("firstName")
          + " - " + empEntity.getProperty("lastName"));
     }
}

この実行結果は次のとおりです。

Employee - Employee(2) - Keisuke - Oyama
Employee - Employee(3) - Grace - Oyama

すなわち、Entity のカインドを Employee としてデータストアに保存したエンティティと、 JDO データクラスの Employee クラスとして定義してデータストアに保存したものが両方取得できています。