ログ日記

作業ログと日記とメモ

CayenneからS2JDBCに戻した

これの続き(?)
http://d.hatena.ne.jp/n314/20111129/1322592759


ブラウザバックでPersistent Object が Hollow 状態になる件は解決した。
http://nishimura-note.blogspot.com/2011/12/way-of-preventing-state-of-persistent.html
検索しても英語の記事すら見つけられなかったのでこっちに書いてみた。


それでも不満が残る。



Cayenneの不満は、何でも自動で実行されすぎるということ。
ObjectContext#commitChanges で、それまでの変更を一気に更新・コミットする、という動作が自分に合わなかった。
not null 制約や プライマリキーのエラーなど、S2JDBCのようにその都度 insert → 例外 という動作の方が分かりやすい。複数のテーブルを変更する場合に commitChanges で例外が起きたら原因を特定しにくい。
また、プライマリキーの情報もS2JDBCのように insert → PK取得 → 何らかの処理 の方が分かりやすい。
一応プライマリキー取得のコードを書いた

public class CayennePkGenerator {
    private List<PkGenerator> gens = CollectionsUtil.newArrayList();
    public Object generatePk(PersistentObject entity, String pkColumn) throws Exception {
        Configuration conf = Configuration.getSharedConfiguration();
        DataNode node = conf.getDomain().lookupDataNode(entity.getObjEntity().getDataMap());

        PkGenerator gen = node.getAdapter().getPkGenerator();
        gens.add(gen);
        DbAttribute a = (DbAttribute) entity.getObjEntity().getDbEntity().getAttribute(pkColumn);
        ObjectId id = entity.getObjectId();

        Object newId = gen.generatePk(node, a);
        Map<String,Object> idMap = id.getReplacementIdMap();
        idMap.put(pkColumn, newId);
        return newId;
    }
    public void reset() {
        for (PkGenerator gen: gens) {
            gen.reset();
        }
        gens.clear();
    }
}

が、単純に考えて 「トランザクション開始」→「insert」→「currval」の方がいい。


また、EntityクラスをWicketのモデル値として使いたい。Sessionでも使いたい。
でもCayenneの Persistent Object はライフサイクルが DataObject と密接に関連しているので手軽に使えない。セッションに格納するPersistent Objectを取得したときの DataObject と、今表示しているページの DataObject は別物だよな…とか考えながらプログラムしないといけない。これはめんどくさい。


単純に構造体のようなものとしてEntityを使いたい。
そんなわけでS2JDBCに戻した。


そして
http://d.hatena.ne.jp/gekimu/20100821#1282398631
ここに書いているように、ページクラスからサービスのselectを呼び出せるようにして、joinやwhereやorderByはページクラスに書けるようにした。


そうすると、例えば

public abstract class S2DataProvider<T extends Serializable> implements IDataProvider<T>{

    protected abstract AutoSelect<T> getAutoSelect();
    protected abstract AutoSelect<T> getOrder(AutoSelect<T> auto);
    @Override
    public final Iterator<? extends T> iterator(int first, int count) {
        return getOrder(getAutoSelect()).limit(count).offset(first).getResultList().iterator();
    }
    @Override
    public final int size() {
        return (int) getAutoSelect().getCount();
    }
    @Override
    public final void detach() {
    }
    @Override
    public IModel<T> model(T arg0) {
        return new Model<T>(arg0);
    }
}

こういうクラスを作ると DataProvider は

        S2DataProvider<Item> provider = new S2DataProvider<Item>() {
            @Override
            protected AutoSelect<Item> getAutoSelect() {
                return ItemService.select()
                    .where(eq(categoryName(), categoryName));
            }
            @Override
            protected AutoSelect<Item> getOrder(AutoSelect<Item> auto) {
                return auto.orderBy(asc(itemName()));
            }
        };

このように書けて、重要な処理に専念できるのではないかと。