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
を使って前後にナビゲートできるわけです.
さらに,これは実装依存なんですが,実際に返ってくる List
は ArrayList
です.なので,ランダムにアクセスしても問題ないわけです.
と考えると,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.java の JavaDoc コメントは変わっていません.前述の通り,最初の問い合せは 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 に組み込んで使う方法が書いてあったりします.
うーみゅ,どこかで試さねば...