Hibernate 入門記 セッションその4 ScrollableResults

ちょっと油断している間に,Hibernate のバージョンは 2.1.6 にまで上がっていたのですね.気づかずに 2.1.4 を使い続けていました.残念!!!!
ともあれ,今回は「9.3.3. Scrollable iteration」へ進みます.相変わらず問い合せ関連です.


今回学習するのは

  • ScrollableResults

です.
こいつを使うには,JDBC ドライバがスクローラブルな ResultSet をサポートしている必要があります.
スクローラブルな ResultSet をサポートした JDBC ドライバと組み合わせると,ScrollableResults を使って問い合せ結果を柔軟にナビゲートできる,ということらしいです.ふむふむ.
ScrollableResults を取得するには,Query

    • ScrollableResults scroll()

を使用します.
ScrollableResults を取得できれば,柔軟にナビゲートすることができます.こんな具合.

    • boolean next()
    • boolean previous()
    • boolean scroll(int i)
    • boolean last()
    • boolean first()
    • void beforeFirst()
    • void afterLast()

まぁ,名前を見れば何をやってくれるか分かりますね.
戻り値が boolean のものは,実際に移動できれば true を,移動できなかった場合は false を返します.
それから,現在の位置を確認するメソッドも用意されています.

    • boolean isFirst()
    • boolean isLast()

現在の位置のオブジェクトを取得するには,次のメソッドを呼び出します.

    • Object[] get()
    • Object get(int i)

そうかぁ,配列が返ってくるんだぁ.なんかいや.


っていうか,このメソッドって必要?
普通に問い合せを行うとその結果は List で返ってきます.ということは,ListIterator を使って前後にナビゲートできるわけです.
さらに,これは実装依存なんですが,実際に返ってくる ListArrayList です.なので,ランダムにアクセスしても問題ないわけです.
と考えると,ScrollableResults による柔軟なナビゲートって,どんな場合にありがたいのでしょう? 今ひとつ分かりません.
あっ.List によるナビゲートは,メモリ上で行われます.つまり,全ての要素はメモリ上に存在しなければなりません.一方 ScrollableResults によるナビゲートは RDBMS によって行われるので,メモリの使用量は少なくて済むかもしれません.
そうか,前々回の Session#iterate(String) や前回の Query#iterate() などにおいらが期待した動きをしてくれるのが,この ScrollableResults かも?


では早速お試ししましょう (早っ).
今回もモデルのみでいけそうなので,テーブルは前回と同じ.
モデルの永続クラスも,マッピングファイルも前回と同じでいいや.
実行用のクラスのみ新たに作成します.

package study;

import java.util.Iterator;

import net.sf.hibernate.Query;
import net.sf.hibernate.ScrollableResults;
import net.sf.hibernate.Session;
import net.sf.hibernate.SessionFactory;
import net.sf.hibernate.cfg.Configuration;

public class Main {
    public static void main(String[] args) {
        try {
            Configuration config = new Configuration();
            SessionFactory factory = config.configure().buildSessionFactory();

            Session session = factory.openSession();

            Model yuri = new Model("Yuri Ebihara", "CanCam");
            session.save(yuri);
            Model yu = new Model("Yu Yamada", "CanCam");
            session.save(yu);
            Model moe = new Model("Moe Oshikiri", "CanCam");
            session.save(moe);
            Model asami = new Model("Asami Usuda", "CanCam");
            session.save(asami);
            Model sayo = new Model("Sayo Aizawa", "ViVi");
            session.save(sayo);
            Model jun = new Model("Jun Hasegawa", "ViVi");
            session.save(jun);
            Model karina = new Model("Karina", "Ray");
            session.save(karina);
            Model kana = new Model("Kana Ishida", "Ray");
            session.save(kana);

            session.flush();
            session.connection().commit();

            session = factory.openSession();

            System.out.println("*** forward ***");
            Query query = session.createQuery("from study.Model");
            ScrollableResults models = query.scroll();
            while (models.next()) {
                System.out.println(models.get(0));
            }

            System.out.println("*** backword ***");
            while (models.previous()) {
                System.out.println(models.get(0));
            }

            System.out.println("*** scroll ***");
            while (models.scroll(3)) {
                System.out.println(models.get(0));
            }
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }
}

見ての通り,順方向に最後までいった後,逆方向に先頭 (の前) まで進んで,最後に 3 行ずつスキップしながら進んでいます.
こいつを実行!!!!

onSave() : Yuri Ebihara
onSave() : Yu Yamada
onSave() : Moe Oshikiri
onSave() : Asami Usuda
onSave() : Sayo Aizawa
onSave() : Jun Hasegawa
onSave() : Karina
onSave() : Kana Ishida
Hibernate: insert into Model (name, magazine, id) values (?, ?, ?)
*** forward ***
Hibernate: select model0_.id as x0_0_ from Model model0_
Hibernate: select model0_.id as id0_, model0_.name as name0_, model0_.magazine as magazine0_ 
           from Model model0_ where model0_.id=?
onLoad() : Yuri Ebihara
Yuri Ebihara : CanCam
Hibernate: select model0_.id as id0_, model0_.name as name0_, model0_.magazine as magazine0_ 
           from Model model0_ where model0_.id=?
onLoad() : Yu Yamada
Yu Yamada : CanCam
Hibernate: select model0_.id as id0_, model0_.name as name0_, model0_.magazine as magazine0_ 
           from Model model0_ where model0_.id=?
onLoad() : Moe Oshikiri
Moe Oshikiri : CanCam
Hibernate: select model0_.id as id0_, model0_.name as name0_, model0_.magazine as magazine0_ 
           from Model model0_ where model0_.id=?
onLoad() : Asami Usuda
Asami Usuda : CanCam
Hibernate: select model0_.id as id0_, model0_.name as name0_, model0_.magazine as magazine0_ 
           from Model model0_ where model0_.id=?
onLoad() : Sayo Aizawa
Sayo Aizawa : ViVi
Hibernate: select model0_.id as id0_, model0_.name as name0_, model0_.magazine as magazine0_ 
           from Model model0_ where model0_.id=?
onLoad() : Jun Hasegawa
Jun Hasegawa : ViVi
Hibernate: select model0_.id as id0_, model0_.name as name0_, model0_.magazine as magazine0_ 
           from Model model0_ where model0_.id=?
onLoad() : Karina
Karina : Ray
Hibernate: select model0_.id as id0_, model0_.name as name0_, model0_.magazine as magazine0_ 
           from Model model0_ where model0_.id=?
onLoad() : Kana Ishida
Kana Ishida : Ray
*** backword ***
Kana Ishida : Ray
Karina : Ray
Jun Hasegawa : ViVi
Sayo Aizawa : ViVi
Asami Usuda : CanCam
Moe Oshikiri : CanCam
Yu Yamada : CanCam
Yuri Ebihara : CanCam
*** scroll ***
Moe Oshikiri : CanCam
Jun Hasegawa : ViVi

ぐはぁっ,またしても SELECT 乱発...
なんてこった,Query#scroll() の動きは Query#iterate() と同様ですか... (;_;)
そんなこと,ドキュメントに書いてないじゃん!!
と思ったら,Query#scroll()JavaDoc に書いてありました.ごめんなさい.

Entities returned as results are initialized on demand. The first SQL query returns identifier only.

そうですか,どうしても ID だけの問い合せがお気に入りですか... これもまた,効果的なキャッシュが前提になっているのでしょうね.
うーみゅ,はやいところキャッシュ周りの学習にたどり着かないと...


でも大丈夫です.この実行結果,実は Hibernate 2.1.4 でのものなのです.
ここで,Hibernate を 2.1.6 にバージョンアップして実行!!!!

onSave() : Yuri Ebihara
onSave() : Yu Yamada
onSave() : Moe Oshikiri
onSave() : Asami Usuda
onSave() : Sayo Aizawa
onSave() : Jun Hasegawa
onSave() : Karina
onSave() : Kana Ishida
Hibernate: insert into Model (name, magazine, id) values (?, ?, ?)
Hibernate: insert into Model (name, magazine, id) values (?, ?, ?)
Hibernate: insert into Model (name, magazine, id) values (?, ?, ?)
Hibernate: insert into Model (name, magazine, id) values (?, ?, ?)
Hibernate: insert into Model (name, magazine, id) values (?, ?, ?)
Hibernate: insert into Model (name, magazine, id) values (?, ?, ?)
Hibernate: insert into Model (name, magazine, id) values (?, ?, ?)
Hibernate: insert into Model (name, magazine, id) values (?, ?, ?)
*** forward ***
Hibernate: select model0_.id as id, model0_.name as name, model0_.magazine as magazine 
           from Model model0_
onLoad() : Yuri Ebihara
Yuri Ebihara : CanCam
onLoad() : Yu Yamada
Yu Yamada : CanCam
onLoad() : Moe Oshikiri
Moe Oshikiri : CanCam
onLoad() : Asami Usuda
Asami Usuda : CanCam
onLoad() : Sayo Aizawa
Sayo Aizawa : ViVi
onLoad() : Jun Hasegawa
Jun Hasegawa : ViVi
onLoad() : Karina
Karina : Ray
onLoad() : Kana Ishida
Kana Ishida : Ray
*** backword ***
Kana Ishida : Ray
Karina : Ray
Jun Hasegawa : ViVi
Sayo Aizawa : ViVi
Asami Usuda : CanCam
Moe Oshikiri : CanCam
Yu Yamada : CanCam
Yuri Ebihara : CanCam
*** scroll ***
Moe Oshikiri : CanCam
Jun Hasegawa : ViVi

なぜ!? なぜに動きが変わる????
今度の問い合せでは,ID だけでなく,ちゃんと名前や雑誌も取得しています.おかげで問い合せは 1 回しか実行されていません.
仕様が変わったの? でも,Query.javaJavaDoc コメントは変わっていません.前述の通り,最初の問い合せは ID しか取ってこないと書いてあります.うーみゅ...
と思ったら,changelog.txt に書いてありました.2.1.5 で修正されたようです.

* improved scroll() query method, now the entire entity is populated from the initial result set (Steve Ebersole)

そうですか,それなら JavaDoc コメントも直そうよ...
念のために確認してみたところ,Session#iterate(String)Query#iterate() の動きは変わっていませんでした.まぁ,changelog.txt にもそんなこと書いてませんしね.
ついでに複数の INSERT が個別にトレース出力されてますね.2.1.4 では一回しか出力されていなかったのですが.
ともあれ,例え入門記でも変更履歴はちゃんと見ろってことですか.残念!!!!
っていうか,この場合は「心より恥じる」の方がピッタリな感じだなぁ.


そういえば,TANGOSOL Coherence を教えてくれた海外駐在の方が来日 (帰国?) していて,今度は GigaSpaces なるものを教えてくれました.これもまたトランザクショナルな分散キャッシュですが,Coherence よりも高機能だとか.その分高価だとか (苦笑).
こいつも Hibernate に対応しているらしく,ドキュメントにも Hibernate に組み込んで使う方法が書いてあったりします.
うーみゅ,どこかで試さねば...