Slim3で作ったTODOリストサービス

ここ最近Slim3とGAE/Jを調べてたんだけど(結構前の時点のslim3-blankとか使って)、結構仕様がかわってるようなので、最新のblankプロジェクトをチェックアウトして、前に試しに作っていたTODOサービスを再実装してみた。一番変わったのはDAOがサポートされたこと。

Google グループ

これが、ほしかった。前のバージョンでは共通で使うデータアクセス用のクラスをどう作っていいのかよく分からなかったので、各コントローラに同じメソッド(findAllとか)作ってたんだけど。その辺の機能はDAOに集約されるようになったから非常に使いやすくなった。
(”クライアント側でfilterやsortしたりしなければならない”部分をDAOに押し込んだことがDAOを導入した理由らしいので、、自分の思ってたのは見当違いかもしれないけど(・・))

また、初心モノさんより動くコードが見たいと要望があったので
Slim3 for GAE/JでAjax - ありの日記
以下のURLでこのプログラムを公開しました。変な実装かもしれないのでその辺はご注意を・・。(こういうときに便利にストレージサービスって何なんだろうって探してて、firestorageを見つけたんだけど、みんなは何使ってるんだろう)
↓↓ダウンロードはこちらから
http://firestorage.jp/download/770b88c3eb2a632c6f7a480369e1ce638a95b95b
※ 注意 ↑このファイルは古いです。最新はhttp://code.google.com/p/hironemu-examples/からチェックアウトできます)


使い方

このアプリを動かすには以下のURLにアクセスしてください。(TOPにリンクをはる元気がなかった。)
http://localhost:8080/member/todo/
1つ目のテキストボックスには日付を「yyyy/MM/dd」で、2つ目のテキストボックスにはTODOの内容を入力します。

DAOについて

DAOを使った例は以下のようになってます。まず、DAOはGenericDaoを継承して作成する。今回独自に実装したメソッドはpublic List findAll(boolean finished)メソッドだけ。保存や削除、キー検索などの基本的なメソッドはGenericDaoクラスに定義されているので、それを使うだけ。

package slim3.dao;

import java.util.ArrayList;
import java.util.List;

import javax.jdo.PersistenceManager;

import org.slim3.jdo.GenericDao;
import org.slim3.util.BeanMap;
import org.slim3.util.BeanUtil;

import slim3.model.Task;
import slim3.model.TaskMeta;

public class TaskDao extends GenericDao<Task> {

    private static final TaskMeta m = new TaskMeta();

    public TaskDao() {
        super(Task.class);
    }

    public TaskDao(PersistenceManager pm) throws NullPointerException {
        super(Task.class, pm);
    }

/*    public List<BeanMap> findAll(boolean finished) {
        List<Task> list =
            from().where(m.finished.eq(finished)).getResultList();
        List<BeanMap> taskList = new ArrayList<BeanMap>(list.size());
        for (Task t : list) {
            BeanMap m = new BeanMap();
            BeanUtil.copy(t, m);
            taskList.add(m);
        }
        return taskList;
    }*/
    // 上のコードはこう書ける
    public List<Task> findAll(boolean finished) {
        return from().where(m.finished.eq(finished)).getResultList();
    }
}

つぎに、コントローラではTaskDaoを作ってメソッドを呼ぶだけ。下の例では、パラメータ"key"で取得したキーを元にデータを検索してきて、そのデータを削除(dao.deletePersistent(t))した後、データを再検索すると言うもの。
削除する前後に「dao.begin();」「dao.commit();」を実行してるのは、これがないと次の検索が行われないためです。これがなくても削除はちゃんとできるんだけど、その後の検索が実行されないんだよね。なぜか。

package slim3.controller.member.todo;

import java.util.logging.Logger;
import org.slim3.controller.Controller;
import org.slim3.controller.Navigation;

import slim3.dao.TaskDao;
import slim3.model.Task;

public class DeleteController extends Controller {

    @SuppressWarnings("unused")
    private static final Logger logger = Logger.getLogger(DeleteController.class.getName());

    private TaskDao dao = new TaskDao();
    
    @Override
    public Navigation run() {

        Task t = dao.find((String) requestScope("key"));
        
//        dao.begin();
//        dao.deletePersistent(t);
//        dao.commit();
        dao.deletePersistentInTx(t);
        
        requestScope("taskList", dao.findAll(false));
        requestScope("doneList", dao.findAll(true));
        
        return forward("index.jsp");
    }
}

DAOの使い方はこんな感じ。簡単だねー。
ちなみに今回アップしているプロジェクトは、以下のURLにあるSVNからチェックアウトした「slim3-blank」のリビジョン321のものです。(2009/07/17)
http://code.google.com/p/slim3/source/checkout

そして、追加したのは、war/WEB-INF/appengine-web.xmlに以下の行を追加したのと(これがないと、tablibのなんかでこけてた)

<sessions-enabled>true</sessions-enabled>

war/WEB-INF/libに「jsonic-1.1.0.jar」を追加した。
jQueryを使うためにwar/javascripts以下に、jquery-1.3.2.min.jsとこのアプリで利用するtodo.jsを置いています。
この辺のAjaxの使った例はここに書いてます。
Slim3 for GAE/JでAjax - ありの日記

あと気をつけないといけない(?)のが
Google グループ
ここにあるようにweb.xmlのversionの部分が2.5になったので、JPSのEL式を使うためにJSPの先頭を以下のように(isELIgnored="false")しないといけないみたい。(gen-controllerで作ったJSPには自動でついてるみたいだけど、いっかい手でJSPを作ったときこのことに気づかなくてはまってしまった。)

<%@page pageEncoding="UTF-8" isELIgnored="false"%>

認証は実装してないけど、このエントリーを見てもらえばできるかも。
Slim3、GAE/JでGoogleアカウントの認証 その2 - ありの日記
あとは、BigTableをうまく扱えるかが問題だねー。今週はその辺を色々試してみよう〜。もうちょい。

追記

ひがさんにコメントもらいTaskDaoのfindAllは上コードのようにシンプルに書けることを教えてもらいました。また、deletePersistentの部分はdeletePersistentInTxですっきり書けるようです(deletePersistentInTxの中で、同じようにbeginとcommitをおこなってます)。
ダウンロードファイルの方は直してないけど、こう書けばすっきり。