【GAE/J】DataStore操作について

現在、Topページで当社で作成された簡易掲示板cboardが公開されています。
GAE/Jについてのコラムということですので、cboardで使っている技術について書いてみたいと思います。

JDOによってGAE/JのDataStoreにアクセス。 追加、検索、更新、削除。 GAE/Jの導入方法は割愛。



GAE/JでJDOはSQLに似ていることもできるけれど、追加はinsertではできませんでした。
そんな訳で、追加はこのメソッドを使用します。
makePersistent(Object)
これはjavax.jdo.PersistenceManagerというパッケージに含まれています。
追加するデータの構成を持ったクラスを呼び出して生成した後、
makePersistentで永続的なデータにしてやる。

今回、使用するデータのクラス構造は以下。
-----------------------------------------------------------------
package cboard.model;

//import文については省略

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

@PrimaryKey
@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Long Thread_ID; //ID

@Persistent
private String Thread_Name; //スレッド名称

@Persistent
private String Thread_Owner; //オーナー名

@Persistent
private Date Thread_Date; //作成日

@Persistent
private int Thread_Attr; //属性

@Persistent
private String Thread_NickName; //オーナーのニックネーム

@Persistent
private String Thread_Email; //オーナーのEメールアドレス

@Persistent
private Date Thread_Last_Date; //最終更新日

@Persistent
@Order(extensions = @Extension(vendorName="datanucleus", key="list-ordering", value="Article_Date desc"))
private ArrayList Thread_ArticleList; //記事一覧(作成日で降順で並んでいる)

@Persistent
private HashSet Thread_MemberList; //メンバー一覧

public ModelThread(String name, String owner, Date date, int attr, User user, Date last_date) {
this.Thread_Name = name;
this.Thread_Owner = owner;
this.Thread_Date = date;
this.Thread_Attr = attr;
if(user != null){
this.Thread_NickName = user.getNickname();
this.Thread_Email = user.getEmail();
}
this.Thread_Last_Date = last_date;
this.Thread_ArticleList = new ArrayList();
this.Thread_MemberList = new HashSet();

}
 :
 :
//各プロパティにはゲッターとセッターが存在するが割愛。
 :
 :

}
-----------------------------------------------------------------

以下、実際に作成したCboardより抜粋。
-----------------------------------------------------------------
public boolean insertThread(String name, String owner, int Attr, User user){
PersistenceManager pm; //これでPersistenceManagerを作成
Date date;
ModelThread thread;
pm = PMF.get().getPersistenceManager();
 :
 :
date = new Date();
thread = new ModelThread(name, owner, date, Attr, user, date); //登録したいデータを持ったクラスを生成
 :
 :
//追加メソッドの呼び出し
pm.makePersistent(thread); //永続データとして追加
 :
 :
pm.close();
return true;

}
-----------------------------------------------------------------

こんな感じでデータを登録することができました。



次は検索。
検索にもいくつかパターンがあるかと思います。

まずは全データを取得してくる方法。
-----------------------------------------------------------------
PersistenceManager pm = PMF.get().getPersistenceManager();
String query = "select from " + ModelThread.class.getName(); //検索するカインドのデータを全部取ってくる
List thread = (List) pm.newQuery(query).execute(); //検索実行
-----------------------------------------------------------------

「String query = "select from "~」のように文字列で全部書く方法のほかに、下記のような方法もありました。
-----------------------------------------------------------------
Query querys = pm.newQuery(ModelThread.class); //結果を取ってくるカインドの指定
List thread = (List)querys.execute(id); //検索実行
-----------------------------------------------------------------

結果で持ってくる列の指定をしたり、Where条件をつけたり、順番を並べ替えたりというのも必要になったので調べました。
クエリに直接、文章を書いてしまう場合には以下。
-----------------------------------------------------------------
"select Thread_ID from " + ModelThread.class.getName() + " order by Thread_ID asc";
-----------------------------------------------------------------
これだと、Thread_IDの列のみを昇順で取ってくることになる。

全データ検索の時と同じく、用意されている関数を使用したのが以下。
-----------------------------------------------------------------
public Map getThreadHeader(long id){
HashMap map = new HashMap();
PersistenceManager pm = PMF.get().getPersistenceManager();

Query querys = pm.newQuery(cboard.model.ModelThread.class); //結果を取ってくるカインドの指定
querys.setFilter("Thread_ID == id"); //Where句設定
querys.declareParameters("Long id"); //上記の型を設定
querys.setOrdering("Thread_Last_Date descending"); //Order Byの指定
querys.setResult("Thread_ID, Thread_Owner, Thread_NickName, Thread_Email, Thread_MemberList, Thread_Attr");
//結果列の指定
List boards = (List)querys.execute(id); //検索実行

 :
 :
pm.close();
return map;
}
-----------------------------------------------------------------
必要なデータだけとってこれるので、よく使っています。
書くのは多少面倒くさいけれど、こっちの方が間違いも少ないように思えるので書き換えるときにいいかもしれない。
返ってくる結果をListとしていますが、結果列として設定しているsetResultの引数の型がそれぞれ違うため。

最後に、このデータだけがほしいっていう場合もあると思うので、その方法を調べてみました。
規定でプライマリキーは一個だけらしく(二個目を追加したらエラーになった)、
その唯一のプライマリキーが分かっていれば、以下のメソッドで取って来れる。
getObjectById(Class,Key)
使い方はこんなの。

-----------------------------------------------------------------
public Map getThreadData(long id){
HashMap map = new HashMap();
PersistenceManager pm = PMF.get().getPersistenceManager();
 :
 :
ModelThread g = pm.getObjectById(ModelThread.class,id); //Keyの部分に分かっているプライマリキーの値を入れる

 :
 :
pm.close();
return map;
}
-----------------------------------------------------------------
これも案外使用頻度の高い関数でした。



更新について。
これは結構手順としては単純。
makePersistent(Object)
最初は戸惑ったけど、追加と同じメソッドで更新できました。
ただし、永続化されるタイミングがPersistenceManagerをcloseするタイミングなので注意。
なお、新規で追加の場合にはmakePersistent()を呼び出したタイミングで永続化されているとドキュメントにありました。
まずは、検索して更新したいデータを取ってきて、セッターで新しい値をセットしたら、
追加関数と同じ関数を、更新したいデータを引数に実行です。
サンプルは上げませんが、上記のサンプルを参考にどうぞ。



最後に削除。
今回、Cboardでは削除は行っていません。
メンバの追加、削除は全て更新の手順を取っているためです。
使用方法を詳しく知るために書いてみました。
更新と似たような手順をとるが、最後に呼び出す関数が違いました。
deletePersistent(Object)
使用したイメージは以下。
-----------------------------------------------------------------
PersistenceManager pm = PMF.get().getPersistenceManager();
ModelThread objThread = pm.getObjectById(ModelThread.class,Key);
pm.deletePersistent(objThread); //永続データの削除
-----------------------------------------------------------------



また、上記の追加、削除に関して一括で行える関数も存在していたのでメモ。
未使用のため、使い方のサンプルは上げません。
一括追加:makePersistentAll(クラスのリスト)
一括削除:deletePersistentAll(クラスのリスト)



以上、だらだらと書きましたが、何かの参考になれば幸いです。