Google App Engine/JavaのData Storeを使ってみる

先のログでは、先走ってGoogle App Engine/Java(GAE/J)のデータストアにおけるカスケード・デリート(Cascade Delete。もしくは、リカーシブ・デリート。Recursive Delete)の問題を書いてしまったのだが、とりあえず、話を整理するために、順をおって、GAE/Jからデータストアを使ってみる

試してみるのは、以下。

  • エンティティー・グループを規定するRootエンティティーをストアする。
  • このRootエンティティーのChildをストアする。
  • キーによって、Childをフェッチ(取得)する。
  • JDOQLによって、Childをフェッチする。
  • Childを削除する。
  • Rootを削除する。

GAE/Jのマニュアルによれば、親を定義しないエンティティーがRootエンティティーとなり、エンティティー・グループを規定する。ストアされたデータに対するトランザクション処理は、エンティティーグループの範囲内という制限があるので、複数のRootエンティティー(=複数のエンティティー・グループ)に対するトランザクションは定義できない。したがって、Rootは出来るだけ少なくする、というのがGAEを使う上での戦略として正しいのだろう。

また、クエリ(JDOQL)を発行する際には、インデックス定義が必要となるので「必ずローカルサーバーでテストせよ」と書かれている。ローカルサーバー(GAE/JのEclipseプラグインに付属)でテストすることによって、必要なインデックスが自動的に作成されるためである。このインデックスは、war下にappengine-generated/datastore-indexes-auto.xmlという名前で作成される。プロジェクトをデプロイする際に、このインデックスも一緒にデプロイされる
正式な(??)インデックス定義は、WEB-INF下のdatastore-indexes.xmlとなるが、デフォルトの状態(プロジェクト作成時)では作成されない。これがない場合、appengine-generated/datastore-indexes-auto.xmlが自動的に作成される。したがって、データをストアを使う場合、ローカルサーバーでのテストが必須となる。

サンプルの概要

以下のような画面から、上の6点についてテストしてみる。


データストアを利用するコードは、(手数を減らすため)全て、サーブレット上に実装した。
(後から気がついたのだが、invokerを使わず、web.xmlサーブレット定義を増やしていく方法は、更新の反映時にEclipseを再起動しなければならず開発の効率が悪い)。

プロジェクトの作成

Web Application Projectを新規に作成する。GWTは使わないので、チェックをはずす。
必要であれば、ロギングの定義を変えるなどする。

Rootエンティティーの登録

まず、Rootとするエンティティーを以下のようにtest.entitiesパッケージに作成した。

package test.entities;

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

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

/*********************************
 * 
 * エンティティーグループのRootエンティティー
 * 
 *     tetsuya_odaka (EzoGP) <br>
 *********************************/

@PersistenceCapable(identityType=IdentityType.APPLICATION)
public class RootTO {

    /*
     * 主キー;
     * (メモ)親エンティティーのキーはKeyか、String。
     */
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    private Key parentKey;
    
    @Persistent
    private String firstName;
    
    @Persistent
    private String lastName;

    /**
     * コンストラクタ
     */
    public RootTO(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    /**
     * setter/getter
     */
    public Key getParentKey() {
        return parentKey;
    }

    public void setParentKey(Key parentKey) {
        this.parentKey = parentKey;
    }

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


そして、これを登録するサーブレットは以下。

package test;

import java.io.IOException;
import java.io.PrintWriter;

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

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

import test.entities.RootTO;

@SuppressWarnings("serial")
public class GaeDataStoreCreateParentServlet extends HttpServlet {
    public void doGet(HttpServletRequest req, HttpServletResponse resp)
        throws IOException {
        
        RootTO to = new RootTO("tetsuya","odaka");
        
        /*
         * keyを生成
         */
        // 「文字」を種(たね)にするときは、数字が先頭だと怒られる。
        Key key =KeyFactory.createKey(RootTO.class.getSimpleName(), "parentKey");
        to.setParentKey(key);
        
        PersistenceManager pm = PMF.get().getPersistenceManager();

        // 親エンティティーを登録
        boolean flg = false;
        try{
            pm.makePersistent(to);
            flg = true;
        }finally{
            pm.close();
        }
        
        resp.setContentType("text/html; charset=utf-8");
        PrintWriter out = resp.getWriter();
        
        out.println("<html><head>");
        out.println("<title>Parentエンティティーの登録</title>");
        out.println("</head>");
        out.println("<body>");
        out.println("<h2>Parentエンティティーの登録</h2>");
        if(flg){
            out.println("Parentエンティティーを登録しました。");
        }else{
            out.println("Parentエンティティーに失敗しました。");
        }
        out.println("</body></html>");
    }
}


ストアの操作には、PersistenceManagerのインスタンスが必要となる。
この目的で、マニュアルに従い、シングルトンにして、スタティック呼び出しでインスタンスの取得が出来るクラスを(PMF)作成する。

package test;

import javax.jdo.JDOHelper;
import javax.jdo.PersistenceManagerFactory;

/**
 * PersisntenceManagerFactory
 * 
 */
public final class PMF {
    private static final PersistenceManagerFactory pmf =
           JDOHelper.getPersistenceManagerFactory("transactions-optional");
    
    private PMF() {}

    public static PersistenceManagerFactory get(){
        return pmf;
    }
    
}

Childエンティティーの登録

上で登録したRootエンティティーの子として、以下のエンティティー(Friends.java)を登録する。実験的に、Rootエンティティー(子から見た親、祖先)のキーをプロパティーに持たせた。

package test.entities;

import java.util.Date;

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

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

/*********************************
 * 
 * Childエンティティー
 * 
 *     tetsuya_odaka (EzoGP) <br>
 *********************************/

@PersistenceCapable(identityType=IdentityType.APPLICATION)
public class Friends {

    // 主キー
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    private Key key;
    
    // 親キー(@Extensionを使って自動でセットする)
    @Persistent
    @Extension(vendorName="datanucleus",key="gae.parent-pk",value="true")
    private Key parentKey;

    @Persistent
    private String firstName;
    
    @Persistent
    private String lastName;

    @Persistent
    private Date entryDate;
    
    /**
     * コンストラクタ
     * 
     */
    public Friends(String firstName, String lastName,Date entryDate) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.entryDate = entryDate;
    }

    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 Key getKey() {
        return key;
    }

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

    public Date getEntryDate() {
        return entryDate;
    }

    public void setEntryDate(Date entryDate) {
        this.entryDate = entryDate;
    }

    public Key getParentKey() {
        return parentKey;
    }

    public void setParentKey(Key parentKey) {
        this.parentKey = parentKey;
    }
}


次は、これをストアするサーブレット。JDOQLの実験をしたいので、3つのエンティティーをストアする。試しに日本語も(エンコーディングを変えることなく)ストアしてみた。
この際、それぞれのエンティティーは、「種類(Kind)=Friends」という括りでストアされる。先のログで「種類=テーブル」と呼んだのは、このためである。
主キーはRootエンティティーからのパス。また、Friends.javaに定義した親エンティティーのキー(プロパティー;parentKey)は、

    @Extension(vendorName="datanucleus",key="gae.parent-pk",value="true")

アノテーションを使うことで、プログラムで明示的にセットする必要はない。

以下が、サーブレットである。

package test;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;

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

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

import test.entities.Friends;
import test.entities.RootTO;

@SuppressWarnings("serial")
public class GaeDataStoreCreateChildrenServlet extends HttpServlet {
    public void doGet(HttpServletRequest req, HttpServletResponse resp)
        throws IOException {
        
        /**
         * エンティティの生成
         */
        Friends child1 = new Friends("jiro","suzuki",new Date());
        Friends child2 = new Friends("taro","sato",new Date());
        // 日本語のテスト
        Friends child3 = new Friends("三郎","山田",new Date());

        // キーの生成(1)
        KeyFactory.Builder kb = new KeyFactory.Builder(RootTO.class.getSimpleName(), "parentKey");
        kb.addChild(Friends.class.getSimpleName(), "friend1");
        Key key1 = kb.getKey();
        // キーのセット(1)
        child1.setKey(key1);

        // キーの生成(2)
        kb = new KeyFactory.Builder(RootTO.class.getSimpleName(), "parentKey");
        kb.addChild(Friends.class.getSimpleName(), "friend2");
        Key key2 = kb.getKey();
        // キーのセット(2)
        child2.setKey(key2);

        // キーの生成(3)
        kb = new KeyFactory.Builder(RootTO.class.getSimpleName(), "parentKey");
        kb.addChild(Friends.class.getSimpleName(), "friend3");
        Key key3 = kb.getKey();
        // キーのセット(3)
        child3.setKey(key3);
        
        boolean flag=false;
        PersistenceManager pm = PMF.get().getPersistenceManager();
        try{
            // (注記)parentKeyは自動でセットされる
            pm.makePersistent(child1);
            pm.makePersistent(child2);
            pm.makePersistent(child3);
            flag=true;
        }finally{
            pm.close();
        }
        
        resp.setContentType("text/html; charset=utf-8");
        PrintWriter out = resp.getWriter();
        
        // 出力部
        out.println("<html><head>");
        out.println("<title>Childエンティティーの登録</title>");
        out.println("</head>");
        out.println("<body>");
        out.println("<h2>Childエンティティーの登録</h2>");

        if(flag){
            out.println("Childエンティティーを3件、登録しました。");
        }else{
            out.println("Childエンティティーの登録に失敗しました。");
        }
        out.println("</body></html>");
    }
}

Childエンティティーをキーを元に取得する。

上で登録したFriends.javaを、主キーを生成して取得する。
主キーは、親(Rootエンティティー)からのパスで表現されるKeyオブジェクトとなるので、KeyFactory.Builderで生成する。
サーブレットとしては、以下を用意した。

package test;

import java.io.IOException;
import java.io.PrintWriter;

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

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

import test.entities.Friends;
import test.entities.RootTO;

@SuppressWarnings("serial")
public class GaeDataStoreGetChildServlet extends HttpServlet {
    public void doGet(HttpServletRequest req, HttpServletResponse resp)
        throws IOException {
        
        PersistenceManager pm 
            = PMF.get().getPersistenceManager();

        /**
         * Childのキーの生成
         * Parent(Root)エンティティーからのパスを含むKeyオブジェクトを生成する
         */
        KeyFactory.Builder kb = new KeyFactory.Builder(RootTO.class.getSimpleName(), "parentKey");
        kb.addChild(Friends.class.getSimpleName(), "friend2");
        Key key = kb.getKey();

        // Childエンティティーの取得
        Friends friend = pm.getObjectById(Friends.class, key);
        
        String firstName = friend.getFirstName();
        String lastName = friend.getLastName();
        String keyString = friend.getKey().toString();
        String parentKeyString = friend.getParentKey().toString();
        
        pm.close();
        resp.setContentType("text/html; charset=utf-8");
        PrintWriter out = resp.getWriter();
        
        out.println("<html><head>");
        out.println("<title>Childエンティティーの取得</title>");
        out.println("</head>");
        out.println("<body>");
        out.println("<h2>キーによるChildエンティティーの取得</h2>");
        out.println("<table border=1>");
        
        out.println("<tr><td>Childエンティティー</td><td>"+Friends.class.getSimpleName()+"</td></tr>");
        out.println("<tr><td>主キー</td><td>"+keyString+"</td></tr>");
        out.println("<tr><td>親キー</td><td>"+parentKeyString+"</td></tr>");
        out.println("<tr><td>名</td><td>"+firstName+"</td></tr>");
        out.println("<tr><td>姓</td><td>"+lastName+"</td></tr>");
        
        out.println("</table></body></html>");
    }
}


この出力結果は以下となる。主キー、親の主キーの形式が確認できる。

JDOQLによるChildの取得

ストアした3つのFriendsエンティティーをJDOQLで取得する。以下は、そのサーブレット
query.execute()で、Friendsエンティティーのリストが取得できるので、それを拡張for文で取り出して、結果を表形式で出力する。

package test;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;

import javax.jdo.PersistenceManager;
import javax.jdo.Query;
import javax.servlet.http.*;

import test.entities.Friends;

@SuppressWarnings("serial")
public class GaeDataStoreQueryServlet extends HttpServlet {
    @SuppressWarnings("unchecked")
    public void doGet(HttpServletRequest req, HttpServletResponse resp)
        throws IOException {
        
        PersistenceManager pm 
            = PMF.get().getPersistenceManager();

        /**
         * JDOQLクエリの発行
         *  firstNameプロパティーの昇順
         */
        Query query = pm.newQuery(Friends.class);
        query.setOrdering("firstName asc");

        resp.setContentType("text/html; charset=utf-8");
        PrintWriter out = resp.getWriter();
        
        out.println("<html><head>");
        out.println("<title>Childエンティティーのクエリー</title>");
        out.println("</head>");
        out.println("<body>");
        out.println("<h2>クエリーによるChildエンティティーの取得</h2>");
        
        try{
            List<Friends> list = (List<Friends>)query.execute();
                for(Friends friend : list){
                    String firstName = friend.getFirstName();
                    String lastName = friend.getLastName();
                    String keyString = friend.getKey().toString();
                    String parentKeyString = friend.getParentKey().toString();
                    out.println("<table border=1>");

                    out.println("<tr><td>Childエンティティー</td><td>"+Friends.class.getSimpleName()+"</td></tr>");
                    out.println("<tr><td>主キー</td><td>"+keyString+"</td></tr>");
                    out.println("<tr><td>親キー</td><td>"+parentKeyString+"</td></tr>");
                    out.println("<tr><td>名</td><td>"+firstName+"</td></tr>");
                    out.println("<tr><td>姓</td><td>"+lastName+"</td></tr>");
      
                    out.println("</table></br>");
                }
        }finally{
            query.closeAll();
            pm.close();
        }
        out.println("</body></html>");
    }
}


結果は、以下の画面となる。
ストアした日本語もエンコードを気にすることなく、復元できている。

トランザクションによるchildエンティティーの削除

上でストアした3つのエンティティーは、主キーのパスが示すとおり、Rootエンティティーが規定するエンティティー・グループに属する。
したがって、1つのトランザクションで取得、削除とった行為が可能となる。
以下は、これを行うサーブレット

package test;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;

import javax.jdo.PersistenceManager;
import javax.jdo.Query;
import javax.servlet.http.*;

import test.entities.Friends;

@SuppressWarnings("serial")
public class GaeDataStoreTransactionServlet extends HttpServlet {
    @SuppressWarnings("unchecked")
    public void doGet(HttpServletRequest req, HttpServletResponse resp)
        throws IOException {
        
        resp.setContentType("text/html; charset=utf-8");
        PrintWriter out = resp.getWriter();
        
        out.println("<html><head>");
        out.println("<title>トランザクション</title>");
        out.println("</head>");
        out.println("<body>");
        out.println("<h2>トランザクションによるchildエンティティーの削除</h2>");
        
        PersistenceManager pm 
        = PMF.get().getPersistenceManager();
        /**
         * Queryオブジェクトの生成
         */
        Query query = pm.newQuery(Friends.class);
        query.setOrdering("firstName asc");

        try{
            // トランザクションの開始
            pm.currentTransaction().begin();
            List<Friends> list = (List<Friends>)query.execute();
            for(Friends friend : list){
                String keyString = friend.getKey().toString();
                String parentKeyString = friend.getParentKey().toString();

                out.println("<table border=1>");
                out.println("<tr><td>childエンティティー</td><td>"+Friends.class.getSimpleName()+"</td></tr>");
                out.println("<tr><td>主キー</td><td>"+keyString+"</td></tr>");
                out.println("<tr><td>親キー</td><td>"+parentKeyString+"</td></tr>");
                out.println("<tr><td colspan=2>削除しました</td>");
                out.println("</table></br>");
                // childエンティティーの削除
                pm.deletePersistent(friend);
            }
            // 全部削除できたらコミット
            pm.currentTransaction().commit();
        }finally{
            if(pm.currentTransaction().isActive()){
                // ロールバック
                pm.currentTransaction().rollback();
            }
            pm.close();
        }
        out.println("</body></html>");
    }
}

Rootエンティティーの削除

以下は、ストアしたRootエンティティーを削除するサーブレット。ここでは、トランザクションを使わずに処理を行う(自動コミットとなる)。

package test;

import java.io.IOException;
import java.io.PrintWriter;

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

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

import test.entities.RootTO;

@SuppressWarnings("serial")
public class GaeDataStoreDeleteParentServlet extends HttpServlet {
    public void doGet(HttpServletRequest req, HttpServletResponse resp)
        throws IOException {
        
        PersistenceManager pm 
            = PMF.get().getPersistenceManager();

        // Rootエンティティーの取得
        Key key = KeyFactory.createKey(RootTO.class.getSimpleName(), "parentKey");
        RootTO to = pm.getObjectById(RootTO.class, key);
        
        // Rootエンティティーの削除
        try{
            pm.deletePersistent(to);
        }finally{
            pm.close();
        }
        
        resp.setContentType("text/html; charset=utf-8");
        PrintWriter out = resp.getWriter();
        
        out.println("<html><head>");
        out.println("<title>Parentエンティティーの削除</title>");
        out.println("</head>");
        out.println("<body>");
        out.println("<h2>Parentエンティティーの削除</h2>");
        out.println("Parentエンティティーを削除しました。");
        out.println("</body></html>");
    }
}

web.xml

以上のサーブレットをwar/WEB-INF/web.xmlに定義する。

<?xml version="1.0" encoding="utf-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">

	<!--//
		Parentエンティティーのストア
	-->
	<servlet>
		<servlet-name>GaeDataStoreCreateParent</servlet-name>
		<servlet-class>test.GaeDataStoreCreateParentServlet</servlet-class>
	</servlet>

	<!--//
		Parentエンティティーの削除
	-->
	<servlet>
		<servlet-name>GaeDataStoreDeleteParent</servlet-name>
		<servlet-class>test.GaeDataStoreDeleteParentServlet</servlet-class>
	</servlet>

	<!--//
		Childエンティティーのストア
	-->
	<servlet>
		<servlet-name>GaeDataStoreCreateChidren</servlet-name>
		<servlet-class>test.GaeDataStoreCreateChildrenServlet</servlet-class>
	</servlet>

	<!--//
		Childエンティティーをキーで取得
	-->
	<servlet>
		<servlet-name>GaeDataStoreGetChild</servlet-name>
		<servlet-class>test.GaeDataStoreGetChildServlet</servlet-class>
	</servlet>

	<!--//
		Childエンティティーをクエリで取得
	-->
	<servlet>
		<servlet-name>GaeDataStoreQuery</servlet-name>
		<servlet-class>test.GaeDataStoreQueryServlet</servlet-class>
	</servlet>

	<!--//
		Childエンティティーをトランザクションで削除
	-->
	<servlet>
		<servlet-name>GaeDataStoreTransaction</servlet-name>
		<servlet-class>test.GaeDataStoreTransactionServlet</servlet-class>
	</servlet>


	<!--//
		以下はサーブレット・マッピング
	-->
	<servlet-mapping>
		<servlet-name>GaeDataStoreCreateParent</servlet-name>
		<url-pattern>/createparent</url-pattern>
	</servlet-mapping>

	<servlet-mapping>
		<servlet-name>GaeDataStoreDeleteParent</servlet-name>
		<url-pattern>/deleteparent</url-pattern>
	</servlet-mapping>

	<servlet-mapping>
		<servlet-name>GaeDataStoreCreateChidren</servlet-name>
		<url-pattern>/createchidren</url-pattern>
	</servlet-mapping>

	<servlet-mapping>
		<servlet-name>GaeDataStoreGetChild</servlet-name>
		<url-pattern>/getchild</url-pattern>
	</servlet-mapping>

	<servlet-mapping>
		<servlet-name>GaeDataStoreQuery</servlet-name>
		<url-pattern>/query</url-pattern>
	</servlet-mapping>

	<servlet-mapping>
		<servlet-name>GaeDataStoreTransaction</servlet-name>
		<url-pattern>/transaction</url-pattern>
	</servlet-mapping>


	<welcome-file-list>
		<welcome-file>index.html</welcome-file>
	</welcome-file-list>
</web-app>

index.html

処理の起点となるindex.htmlを以下にしめす。

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<!-- The HTML 4.01 Transitional DOCTYPE declaration-->
<!-- above set at the top of the file will set     -->
<!-- the browser's rendering engine into           -->
<!-- "Quirks Mode". Replacing this declaration     -->
<!-- with a "Standards Mode" doctype is supported, -->
<!-- but may lead to some differences in layout.   -->

<html>
  <head>
    <meta http-equiv="content-type" content="text/html; charset=UTF-8">
    <title>GAE:データストアのテスト</title>
  </head>

  <body>
    <h2>GAE/Jのデータストアの基本的なテスト</h2>
	
    <table>
      <tr>
        <td><a href="createparent"/>parent(root)エンティティーをストアします</td>
      </tr>
      <tr>
        <td><a href="createchidren"/>3つのchildエンティティーをストアします</td>
      </tr>
      <tr>
        <td><a href="getchild"/>childエンティティーをキーを元に取得します</td>
      </tr>
      <tr>
        <td><a href="query"/>全てのchildエンティティーをJDOQLのクエリで取得します</td>
      </tr>
    </table>
    <br><br>
    <table>
      <tr>
        <td><a href="transaction"/>トランザクションを使って全てのchildエンティティーを削除します</td>
      </tr>
      <tr>
        <td><a href="deleteparent"/>parent(root)エンティティーを削除します</td>
      </tr>
    </table>
  </body>
</html>

ローカル環境でのテスト

先に述べたように、データストアを利用する際にはローカルサーバーでテストを行うのが無難。
ローカルサーバーでストアのテストをすると、war/WEB-INF下にappengine-generatedディレクトリが作成されて、

  • datastore-indexes-auto.xml;自動作成されたインデックス
  • local_db.bin;ローカル環境でストアしたエンティティーを格納するデータストア

ができる。これらは、ローカル環境で削除&再作成が自由にできる。以下は、ここまでのディレクトリ構成のスクリーンショット

デプロイと管理コンソール

ここまで準備ができれば、GAE環境にデプロイすればよい。
GAEにデプロイすると、管理コンソールでindexの状況をみたり、ストアされたエンティティーを更新したりすることができるようになる。(管理コンソールの左ペインの、「Data Store」下にある「indexes」、「Data Viewer」がそれである。
GAE/Jのインデックスの更新は、逐次的に行われる。デプロイ後、インデックスのStatusが、全て「Serverd」となれば、Indexesが使えることになる。
また、Data Viewerは、ストアの状況が反映されるまでに、しばらく時間がかかる(この例では5分位だった)。なので、ストアしたデータが現れない場合、時間をおいてみる必要がある。
以下は、Data Viewerのスクリーンショット。プロパティーにセットした日本語も表示される。