Google Apps Engine/JavaのData Storeで所有関係を作ってみる(1:1)

Google Apps Engine/Java(GAE/J)のData Storeで1:1の所有関係を作ってみる。

先のログに書いたように、所有関係も(主キー上で表現される)「ルートエンティティーからのパス」概念に帰着される。

公式HP(こことかここ)をみると、所有側インスタンスの1プロパティーに、被所有のインスタンス(の参照)を代入し、所有側をストアすることで、所有関係が成立する、とある。この際、ストアされるのは、所有側のインスタンス(エンティティー)と被所有のインスタンス(エンティティー)の2つとなり、被所有のインスタンスのキーは(自動的に所有側の子孫として)設定される。

データをストア/取り出し/削除するサンプルは、前回同様にサーブレット上に実装する。今回は、1:1の所有関係にまつわる操作を、以下の画面から行う。


この画面からは、

  • Rootエンティティーのストア
  • 所有側エンティティー(Friends)、被所有のエンティティー(FriendsInfo)のストア
  • 主キーを生成して、所有側エンティティーを取り出す、同時にそれに紐付く被所有のエンティティーのプロパティー値をとりだす
  • 主キーを生成して、被所有のエンティティーを取り出す。
  • JDOQLを使って、所有/被所有のエンティティーの一覧を作成する。
  • 所有側のエンティティーを削除する。(カスケードデリートのテスト
  • 所有側のエンティティーと被所有のエンティティーを、それぞれ明示的に削除する。
  • Rootエンティティーを削除する。

を行うこととした。

プロジェクトの作成

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エンティティー
 * 
 * @author
 *     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;
    }

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

Friendsエンティティー

次に、Rootで規定されるエンティティー・グループのエンティティーとして、Friends.javaを以下のように作成した。
このエンティティーは、所有関係において所有者(所有する側)となる。

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;


/*********************************
 * 
 * エンティティー(所有)
 * 
 * @author
 *     tetsuya_odaka (EzoGP) <br>
 *********************************/

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

    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    private Key key;
    
    @Persistent
    @Extension(vendorName="datanucleus",key="gae.parent-pk",value="true")
    private Key parentKey;

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

    @Persistent
    private FriendsInfo friendsInfo;

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

    /**
     * setter/getter
     */
    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 Date getEntryDate() {
        return entryDate;
    }

    public void setEntryDate(Date entryDate) {
        this.entryDate = entryDate;
    }
    
    public FriendsInfo getFriendsInfo() {
        return friendsInfo;
    }

    public void setFriendsInfo(FriendsInfo friendsInfo) {
        this.friendsInfo = friendsInfo;
    }

    public Key getKey() {
        return key;
    }

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

    public Key getParentKey() {
        return parentKey;
    }

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

FriendsInfoエンティティー

最後に、Friends.java所有されるエンティティ(被所有)として、FriendsInfo.javaを以下のように作成した。

package test.entities;

import java.util.Date;

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

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


/*********************************
 * 
 * エンティティー(被所有)
 * 
 *     tetsuya_odaka (EzoGP) <br>
 *********************************/

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

    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    private Key key;
    
    @Persistent
    @Extension(vendorName="datanucleus",key="gae.parent-pk",value="true")
    private Key parentKey;

    @Persistent
    private String attrName;

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

    /**
     * setter/getter
     */
    public String getAttrName() {
        return attrName;
    }

    public void setAttrName(String attrName) {
        this.attrName = attrName;
    }

    public Date getEntryDate() {
        return entryDate;
    }

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

    public Key getKey() {
        return key;
    }

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

    public Key getParentKey() {
        return parentKey;
    }

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

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 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>Rootエンティティーの登録</title>");
        out.println("</head>");
        out.println("<body>");
        out.println("<h2>Rootエンティティーの登録</h2>");
        if(flg){
            out.println("Rootエンティティーを登録しました。");
        }else{
            out.println("Rootエンティティーに失敗しました。");
        }
        out.println("</body></html>");
    }
}

Friends, FriendsInfoエンティティーの登録

以下のサーブレットで、所有者となるFriendsと被所有者のFriendsInfoのストアを行う。

ここで注目すべきは

  • アノテーション定義により、各エンティティーの「親キー」が自動的にセットされること
  • 被所有のエンティティーの主キーには、自動的にRootからのパスが設定されること

である。

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.FriendsInfo;
import test.entities.RootTO;

@SuppressWarnings("serial")
public class GaeDataStoreCreateChildrenServlet extends HttpServlet {
    public void doGet(HttpServletRequest req, HttpServletResponse resp)
        throws IOException {
        
        /**
         * 所有されるエンティティの生成
         */
        FriendsInfo fi1 = new FriendsInfo("属性1",new Date());
        FriendsInfo fi2 = new FriendsInfo("属性2",new Date());
        FriendsInfo fi3 = new FriendsInfo("属性3",new Date());
        
        // fiキーの生成(1)
        KeyFactory.Builder kb = new KeyFactory.Builder(FriendsInfo.class.getSimpleName(), "info1");
        Key key1 = kb.getKey();

        // fiキーのセット(1)
        fi1.setKey(key1);
        
        // fiキーの生成(2)
        kb = new KeyFactory.Builder(FriendsInfo.class.getSimpleName(), "info2");
        Key key2 = kb.getKey();

        // fiキーのセット(2)
        fi2.setKey(key2);

        // fiキーの生成(3)
        kb = new KeyFactory.Builder(FriendsInfo.class.getSimpleName(), "info3");
        Key key3 = kb.getKey();

        // fiキーのセット(3)
        fi3.setKey(key3);
        
        PersistenceManager pm = PMF.get().getPersistenceManager();
       
        /**
         * 所有する側のエンティティの生成
         */
        Friends child1 = new Friends("次郎","鈴木",new Date());
        Friends child2 = new Friends("太郎","佐藤",new Date());
        Friends child3 = new Friends("三郎","山田",new Date());

        // キーの生成(1)
        kb = new KeyFactory.Builder(RootTO.class.getSimpleName(), "parentKey");
        kb.addChild(Friends.class.getSimpleName(), "friend1");
        Key key4 = kb.getKey();

        // キーのセット(1)
        child1.setKey(key4);

        // キーの生成(2)
        kb = new KeyFactory.Builder(RootTO.class.getSimpleName(), "parentKey");
        kb.addChild(Friends.class.getSimpleName(), "friend2");
        Key key5 = kb.getKey();

        // キーのセット(2)
        child2.setKey(key5);

        // キーの生成(3)
        kb = new KeyFactory.Builder(RootTO.class.getSimpleName(), "parentKey");
        kb.addChild(Friends.class.getSimpleName(), "friend3");
        Key key6 = kb.getKey();

        // 子のキーのセット(3)
        child3.setKey(key6);
        
        // FriendsInfoのセット
        // (注)こうしてセットしてmakePersistentすれば、エンティティー
        // が生成され、キーには親のキーが負荷される。(親キーもセットされる。entity beanの設定より)
        child1.setFriendsInfo(fi1);
        child2.setFriendsInfo(fi2);
        child3.setFriendsInfo(fi3);
        
        boolean flag=false;
        try{
            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>Friends,FriendsInfoエンティティーの登録</title>");
        out.println("</head>");
        out.println("<body>");
        out.println("<h2>Friends,FriendsInfoエンティティーの登録</h2>");

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

主キーによりFriendsエンティティーを取得する。

以下のサーブレットにより主キーを生成して、Data StoreからFriendsエンティティーを取得する。
主キーが、Rootからのパスを含んだKeyオブジェクトとなるため、KeyFactory.BuilderにaddChildしてキーを生成する。

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 GaeDataStoreGetFriendsServlet extends HttpServlet {
    public void doGet(HttpServletRequest req, HttpServletResponse resp)
        throws IOException {
        
        PersistenceManager pm 
            = PMF.get().getPersistenceManager();

        // キーの生成
        KeyFactory.Builder kb = new KeyFactory.Builder(RootTO.class.getSimpleName(), "parentKey");
        kb.addChild(Friends.class.getSimpleName(), "friend2");
        Key key = kb.getKey();

        // オブジェクトの取得
        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();
        String attrName = friend.getFriendsInfo().getAttrName();
        
        pm.close();
        resp.setContentType("text/html; charset=utf-8");
        PrintWriter out = resp.getWriter();
        
        out.println("<html><head>");
        out.println("<title>Friendsエンティティーの取得</title>");
        out.println("</head>");
        out.println("<body>");
        out.println("<h2>キーによるFriendsエンティティーの取得</h2>");
        out.println("<table border=1>");
        
        out.println("<tr><td>Friendsエンティティー</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("<tr><td>属性名</td><td>"+attrName+"</td></tr>");
        
        out.println("</table></body></html>");
    }
}


これを実行した結果は、以下の画面となる。

主キーによりFriendsInfoエンティティーを取得する。

以下のサーブレットにより、主キーを生成して、Data StoreからFriendsInfoエンティティーを取得する。FriendsInfoはFriendsの下位に位置づけられるため、キーの生成はRoot=>Friends=>FriendsInfoとなる。

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.FriendsInfo;
import test.entities.RootTO;

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

        // キーの生成
        KeyFactory.Builder kb = new KeyFactory.Builder(RootTO.class.getSimpleName(), "parentKey");
        kb.addChild(Friends.class.getSimpleName(), "friend2");
        kb.addChild(FriendsInfo.class.getSimpleName(), "info2");
        Key key = kb.getKey();

        // オブジェクトの取得
        FriendsInfo friendInfo = pm.getObjectById(FriendsInfo.class, key);
        
        String attrName = friendInfo.getAttrName();
        String keyString = friendInfo.getKey().toString();
        String parentKeyString = friendInfo.getParentKey().toString();
        
        pm.close();
        resp.setContentType("text/html; charset=utf-8");
        PrintWriter out = resp.getWriter();
        
        out.println("<html><head>");
        out.println("<title>FriendsInfoエンティティーの取得</title>");
        out.println("</head>");
        out.println("<body>");
        out.println("<h2>キーによるFriendsInfoエンティティーの取得</h2>");
        out.println("<table border=1>");
        
        out.println("<tr><td>FriendsInfoエンティティー</td><td>"+FriendsInfo.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>"+attrName+"</td></tr>");
        
        out.println("</table></body></html>");
    }
}


これを実行した結果は、以下の画面となる。

JDOQLによりFriends, FriendsInfoエンティティーを取得する。

JDOQLのクエリにより、ストアされているFriendsエンティティー、FriendsInfoエンティティーを取り出して表示する。

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;
import test.entities.FriendsInfo;

@SuppressWarnings("serial")
public class GaeDataStoreQueryServlet 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>Friends,FriendsInfoエンティティーのクエリー</title>");
        out.println("</head>");
        out.println("<body>");
        out.println("<h2>クエリーによるFriends,FriendsInfoエンティティーの取得</h2>");
        
        out.println("<h3>Friendsエンティティー</h3>");
        PersistenceManager pm 
                = PMF.get().getPersistenceManager();

        /**
         * Friendsエンティティーに対するクエリの実行
         */
        Query query = pm.newQuery(Friends.class);
        query.setOrdering("firstName asc");

        try{
            List<Friends> list = (List<Friends>)query.execute();
                for(Friends friend : list){
                    String firstName = friend.getFirstName();
                    String lastName = friend.getLastName();
                    String attrName = friend.getFriendsInfo().getAttrName();
                    String keyString = friend.getKey().toString();
                    String parentKeyString = friend.getParentKey().toString();
                    out.println("<table border=1>");

                    out.println("<tr><td>Friendsエンティティー</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("<tr><td>属性名</td><td>"+attrName+"</td></tr>");
                    out.println("</table></br>");
                }
        }finally{
            query.closeAll();
        }

        out.println("<br><br>");
        out.println("<h3>FriendsInfoエンティティー</h3>");

        /**
         * FriendsInfoエンティティーに対するクエリの実行
         */
        query = pm.newQuery(FriendsInfo.class);
        try{
            List<FriendsInfo> list = (List<FriendsInfo>)query.execute();
                for(FriendsInfo friendsInfo : list){
                    String keyString = friendsInfo.getKey().toString();
                    String parentKeyString = friendsInfo.getParentKey().toString();
                    String attrName = friendsInfo.getAttrName();
                    out.println("<table border=1>");
                    out.println("<tr><td>FriendsInfoエンティティー</td><td>"+FriendsInfo.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>"+attrName+"</td></tr>");
                    out.println("</table></br>");
                }
        }finally{
            query.closeAll();
            pm.close();
        }
        out.println("</body></html>");
    }
}


これを実行した結果は、以下の画面となる。


Friendsエンティティーを全て削除する(カスケード削除)

以下のプログラムにより、Friendsエンティティーを全て削除する。
この処理にともなって、被所有の関係にあるFriendsInfoが削除される(カスケード削除、リカーシブ削除)されることを期待したが、FriendsInfoが残ってしまう、という結果を得た。
(これは、この処理の実行後、JDOQLを画面から発行すれば確認できる)

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 GaeDataStoreCascadeDaleteServlet 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>エンティティーのカスケードデリート</h2>");
        
        PersistenceManager pm 
            = PMF.get().getPersistenceManager();

        Query query = pm.newQuery(Friends.class);

        try{
            List<Friends> list = (List<Friends>)query.execute();
            pm.deletePersistentAll(list);
        }finally{
            query.closeAll();
            pm.close();
        }
        out.println("</body></html>");
    }
}

トランザクションを使ってFriends,FrindsInfoエンティティーを全て削除する

Friends, FriendsInfoが同一のエンティティー・グループに属し、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;
import test.entities.FriendsInfo;

@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>トランザクションによるFriends,FriendsInfoエンティティーの削除</h2>");
        
        PersistenceManager pm 
            = PMF.get().getPersistenceManager();

        Query query = pm.newQuery(Friends.class);

        try{
            // トランザクションの開始
            pm.currentTransaction().begin();
            List<Friends> list = (List<Friends>)query.execute();
            for(Friends friend : list){
                FriendsInfo friendsInfo = friend.getFriendsInfo();
                // FriendsInfoの削除
                pm.deletePersistent(friendsInfo);
                // Friendsの削除
                pm.deletePersistent(friend);
            }
            // 全部削除できたらコミット
            pm.currentTransaction().commit();
            out.println("処理が正常に終了しました。");
        }finally{
            if(pm.currentTransaction().isActive()){
                // ロールバック
                pm.currentTransaction().rollback();
                out.println("処理が異常終了しました。");
            }
            query.closeAll();
            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();

        // オブジェクトの取得
        Key key = KeyFactory.createKey(RootTO.class.getSimpleName(), "parentKey");
        RootTO to = pm.getObjectById(RootTO.class, key);
        
        // オブジェクトの削除
        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>Rootエンティティーの削除</title>");
        out.println("</head>");
        out.println("<body>");
        out.println("<h2>Rootエンティティーの削除</h2>");
        out.println("Rootエンティティーを削除しました。");
        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(Root)エンティティーのストア
	-->
	<servlet>
		<servlet-name>GaeDataStoreCreateParent</servlet-name>
		<servlet-class>test.GaeDataStoreCreateParentServlet</servlet-class>
	</servlet>

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

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

	<!--//
		主キーによる、Friendsエンティティーの取得
	-->
	<servlet>
		<servlet-name>GaeDataStoreGetFriends</servlet-name>
		<servlet-class>test.GaeDataStoreGetFriendsServlet</servlet-class>
	</servlet>

	<!--//
		主キーによる、FriendsInfoエンティティーの取得
	-->
	<servlet>
		<servlet-name>GaeDataStoreGetFriendsInfo</servlet-name>
		<servlet-class>test.GaeDataStoreGetFriendsInfoServlet</servlet-class>
	</servlet>

	<!--//
		JDOQLによる、Friends,FriendsInfoエンティティーの取得
	-->
	<servlet>
		<servlet-name>GaeDataStoreQuery</servlet-name>
		<servlet-class>test.GaeDataStoreQueryServlet</servlet-class>
	</servlet>

	<!--//
		Friends,FriendsInfoエンティティーの削除(トランザクション)
	-->
	<servlet>
		<servlet-name>GaeDataStoreTransaction</servlet-name>
		<servlet-class>test.GaeDataStoreTransactionServlet</servlet-class>
	</servlet>

	<!--//
		Friendsエンティティーの削除(カスケード削除のテスト)
	-->
	<servlet>
		<servlet-name>GaeDataStoreCascadeDalete</servlet-name>
		<servlet-class>test.GaeDataStoreCascadeDaleteServlet</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>GaeDataStoreGetFriends</servlet-name>
		<url-pattern>/getfriends</url-pattern>
	</servlet-mapping>

	<servlet-mapping>
		<servlet-name>GaeDataStoreGetFriendsInfo</servlet-name>
		<url-pattern>/getfriendsinfo</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>

	<servlet-mapping>
		<servlet-name>GaeDataStoreCascadeDalete</servlet-name>
		<url-pattern>/cascadedelete</url-pattern>
	</servlet-mapping>

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

index.html

war/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>データストアの所有関係(1:1)のテスト</h2>
	
    <table>
      <tr>
        <td><a href="createparent"/>Rootエンティティーをストアします</td>
      </tr>
      <tr>
        <td><a href="createchidren"/>Friends,FriendsInfoエンティティーをストアします</td>
      </tr>
      <tr>
        <td><a href="getfriends"/>Friendsエンティティーをキーを元に取得します</td>
      </tr>
      <tr>
        <td><a href="getfriendsinfo"/>FriendsInfoエンティティーをキーを元に取得します</td>
      </tr>
      <tr>
        <td><a href="query"/>JDOQLでクエリでFriends,FriendsInfoエンティティーを取得します</td>
      </tr>
    </table>
    <br><br>
    <table>
      <tr>
        <td><a href="cascadedelete"/>Friendsエンティティーを全て削除します(カスケード削除)</td>
      </tr>
      <tr>
        <td><a href="transaction"/>トランザクションを使ってFriends,FriendsInfoエンティティーを全て削除します</td>
      </tr>
      <tr>
        <td><a href="deleteparent"/>Rootエンティティーを削除します</td>
      </tr>
    </table>
  </body>
</html>

ローカルでの実行とデプロイ

indexを生成するためローカル環境で実行し、war/WEB-INF/appengine-generated/datastore-indexes-auto.xmlを生成する。
その後、GEA/Jにデプロイする。