Hibernate 入門記 セッションその3 Query
前回に引き続き問い合せのお勉強です.今回は「9.3.2. The Query interface」へ進みます.
取得する結果セットの開始位置や最大行数を指定したい場合には,
Query
という interface
を使うことができるようです.
こいつを取得するには,Session
の
-
Query createQuery(String)
を使用します.
Query
のインスタンスを取得できれば,次のメソッドを呼び出すことで,フェッチする最初の行を指定することができます.
-
Query setFirstResult(int firstResult)
引数で渡すのは,0 からのオフセットです.戻り値は Query
ということで自分自身ですね.
また,次のメソッドを呼び出すことで,フェッチする最大の行数を指定することができます.
-
Query setMaxResults(int maxResults)
まぁ,どうってことはないでしょう.
Query
のプロパティを設定したら,実際に問い合せを行います.それには,Query
の
-
List list()
を呼び出します.あるいは,
-
Iterator iterate()
を使うこともできます.
Query
を使うことでありがたいのは,問い合せ文字列を外部化できるということです.これは名前付き問い合せ (named query) と呼ばれるようです.
問い合せ文字列は,マッピングファイルに記述します.<hibernate-mapping>
要素の子として,<class>
要素などの後に
<query>
要素
を記述します.こいつは,次の属性を持っています.
name
- この問い合せの名前を指定します.必須です.
問い合せの名前は,永続クラスの完全限定名で修飾することがお勧めのようです.たぶん...
そして,<query>
要素の内容として,問い合せ文字列を記述します.不等号記号を使うことが多いので,CDATA セクションを使うことを忘れないようにしましょう.
マッピングファイルに記述された問い合せ文字列を使うには,Session
の
-
Query getNamedQuery(String queryName)
を使います.引数はもちろん <query>
要素の name
属性で指定した名前です.
外部化したものであれ,していないものであれ,問い合せ文字列には,パラメータを含むこともできます.
パラメータは,名前のないものと名前付きのものがあります.
名前のないパラメータは,JDBC と同様に問い合せ文字列中に ?
で記述します.このパラメータに値を設定するには,Query
の
-
Query setXxx(int position, xxx val)
を使います.
注意しなければならい点があります.第一引数の position
は,JDBC API のように 1 からではなく,0 から始まります.間違いやすそうだなぁ.
ということで(?),Hibernate は名前付きのパラメータもサポートしています.
名前付きのパラメータは,問い合せ文字列中に :名前
という形式で指定します.この名前付きのパラメータに値を設定するには,Query
の
-
Query setXxx(String name, xxx val)
を使います.
名前付きパラメータを使うと,問い合せ文字列中にパラメータが現れる順番と無関係にパラメータを設定することができます.
また,問い合せ文字列中に同じ名前のパラメータを何度も使うこともできるようです.その場合でも,パラメータの設定は一回だけで済むはず.
ということで,ここまでの分をお試ししましょう.
今回は関連は不要なので,単純にモデルだけ使ってみます.
CREATE TABLE MODEL ( ID INTEGER IDENTITY PRIMARY KEY, NAME VARCHAR, MAGAZINE VARCHAR )
そしてモデルの永続クラス.
package study; import java.io.Serializable; import net.sf.hibernate.CallbackException; import net.sf.hibernate.Lifecycle; import net.sf.hibernate.Session; public class Model implements Lifecycle { int id = -1; String name; String magazine; public Model() { } public Model(String name, String magazine) { this.name = name; this.magazine = magazine; } public String toString() { return name + " : " + magazine; } public void onLoad(Session s, Serializable id) { System.out.println("onLoad() : " + name); } public boolean onSave(Session s) throws CallbackException { System.out.println("onSave() : " + name); return false; } public boolean onUpdate(Session s) throws CallbackException { System.out.println("onUpdate() : " + name); return false; } public boolean onDelete(Session s) throws CallbackException { System.out.println("onDelete() : " + name); return false; } }
特にどうということもなく.
そしてモデルのマッピングファイル.
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 2.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd" > <hibernate-mapping auto-import="false" package="study"> <class name="Model"> <id name="id" access="field" unsaved-value="-1"> <generator class="increment"/> </id> <property name="name" access="field"/> <property name="magazine" access="field"/> </class> <query name="study.Model.byName"><![CDATA[ from study.Model model where model.name = ? ]]></query> <query name="study.Model.byMagazine"><![CDATA[ from study.Model model where model.magazine = :magazine ]]></query> </hibernate-mapping>
二つの名前付き問い合せを記述しています.
こいつを hibernate.cfg.xml
に記述します.
そして実行用のクラス.
package study; import java.util.Iterator; import net.sf.hibernate.Query; 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("*** all ***"); Query query = session.createQuery("from study.Model"); query.setFirstResult(3); query.setMaxResults(4); Iterator it = query.iterate(); while (it.hasNext()) { System.out.println(it.next()); } System.out.println("*** byName ***"); query = session.getNamedQuery("study.Model.byName"); query.setString(0, "Yuri Ebihara"); it = query.iterate(); while (it.hasNext()) { System.out.println(it.next()); } System.out.println("*** byMagazine ***"); query = session.getNamedQuery("study.Model.byMagazine"); query.setString("magazine", "CanCam"); it = query.iterate(); while (it.hasNext()) { System.out.println(it.next()); } } catch (Throwable e) { e.printStackTrace(); } } }
ここでは,3 つの問い合せオブジェクトを作成しています.
最初の問い合せは,Session#createQuery(String)
を使っています.問い合せ文字列自体は全件問い合せなのですが,オフセット 3 からの 4 件だけを取得します.
残りの二つの問い合せは,Session#getNamedQuery(String)
で名前付き問い合せを使っています.
名前付き問い合せのうち,byName
では名前なしのパラメータを,byMagazine
では名前付きのパラメータを使っています.
こいつを実行!!!!
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 (?, ?, ?) *** all *** Hibernate: select limit ? ? 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() : Asami Usuda Hibernate: select model0_.id as id0_, model0_.name as name0_, model0_.magazine as magazine0_ from Model model0_ where model0_.id=? onLoad() : Sayo Aizawa Asami Usuda : CanCam Hibernate: select model0_.id as id0_, model0_.name as name0_, model0_.magazine as magazine0_ from Model model0_ where model0_.id=? onLoad() : Jun Hasegawa Sayo Aizawa : ViVi Hibernate: select model0_.id as id0_, model0_.name as name0_, model0_.magazine as magazine0_ from Model model0_ where model0_.id=? onLoad() : Karina Jun Hasegawa : ViVi Karina : Ray *** byName *** Hibernate: select model0_.id as x0_0_ from Model model0_ where (model0_.name=? ) 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 *** byMagazine *** Hibernate: select model0_.id as x0_0_ from Model model0_ where (model0_.magazine=? ) Hibernate: select model0_.id as id0_, model0_.name as name0_, model0_.magazine as magazine0_ from Model model0_ where model0_.id=? onLoad() : Yu Yamada Yuri Ebihara : CanCam Hibernate: select model0_.id as id0_, model0_.name as name0_, model0_.magazine as magazine0_ from Model model0_ where model0_.id=? onLoad() : Moe Oshikiri Yu Yamada : CanCam Moe Oshikiri : CanCam Asami Usuda : CanCam
んー? うまくいった... のか?
問い合せ結果 (青字) そのものは悪くないようです.しかし... 問い合せ (太字) が多すぎるような?
よく見ると,まずはモデルの ID だけを問い合せて,その結果行ごとにもういちど問い合せを発行しているようです.なぜに??
Query#iterate()
ってそういうもの? そんなバカなことしないでぇ〜.
...
ぐはぁっ,前回の Session#iterate(String)
でも同じ動きをしてるじゃないですか!! 気づかなかった... なぜこんなことを見逃したのかわからないし,全く記憶にないが,
相当頭が悪かったことは確かである。
過去の自分に幻滅。
すこし試行錯誤してみたところ,Query#iterate()
ではなく Query#list()
を使うと普通に問い合せしてくれるようです.
その実行結果 (問い合せ以降のみ).
*** all *** Hibernate: select limit ? ? model0_.id as id, model0_.name as name, model0_.magazine as magazine from Model model0_ onLoad() : Asami Usuda onLoad() : Sayo Aizawa onLoad() : Jun Hasegawa onLoad() : Karina Asami Usuda : CanCam Sayo Aizawa : ViVi Jun Hasegawa : ViVi Karina : Ray *** byName *** Hibernate: select model0_.id as id, model0_.name as name, model0_.magazine as magazine from Model model0_ where (model0_.name=? ) onLoad() : Yuri Ebihara Yuri Ebihara : CanCam *** byMagazine *** Hibernate: select model0_.id as id, model0_.name as name, model0_.magazine as magazine from Model model0_ where (model0_.magazine=? ) onLoad() : Yu Yamada onLoad() : Moe Oshikiri Yuri Ebihara : CanCam Yu Yamada : CanCam Moe Oshikiri : CanCam Asami Usuda : CanCam
うん,この方がステキっ★
っていうか,Query#iterate()
ってこういうものなわけ!?
The iterator will load objects on demand, using the identifiers returned by an initial SQL query (n+1 selects total).
むぎゅう,ちゃんと書いてあるし... おいらの目は節穴でした.ごめんなさい.m(__)m
ということで,基本はやっぱり List Session#find(String)
または List Query#list()
ということですね.
特別な理由がない限り,Iterator Session#iterate(String)
や Iterator Query#iterate()
は使うべきではない,と.了解です〜.
ぢぐじょー,「ぐはぁっ」自体はお約束なのでいいとしても (いいのか?),一回遅れで「ぐはぁっ」するのは情けない.おかげで
カナリオチコミモード
…いわゆるKモードに入ってしまいまして,(いわないよ)
あーっもぉっ!!!!
もっと優秀な頭脳を我に!!