Hibernate 入門記 問い合せ その3 式いろいろ
『たすけてくださぁぁぁぁぁい!!!!!』
書いてみたかっただけです.m(__)m
謎めき系ではないこの日記としては,これもちゃんと解説しておかねば! ということに気づきました.
これは単なるセカチューではありません.「『「何でもセカチュー風に叫んでみる」遊び』をしている眞鍋かをり風に書いてみる」遊びをしているのです.
元ネタはご存じ,「眞鍋かをりのここだけの話」です.
08/25 の冒頭も元ネタは「眞鍋かをりのここだけの話」です.突然「ちがうの。」とかお姉さん言葉になったのはそのためです.そこんとこよろしくです.
そんなわけで (どんなわけで?),何が助けて欲しいかって,前々回に作成したテーブルや永続クラスではお試しできないものがあったわけです.
そこで,今回はサンプルコードを変更します.ほらぁ,助けて欲しくなるでしょう?
そんなわけで (どんなわけで?),新しいテーブル定義.
CREATE TABLE MAGAZINE ( ID INTEGER IDENTITY PRIMARY KEY, NAME VARCHAR, PUBLISHER VARCHAR ) CREATE TABLE MODEL ( ID INTEGER IDENTITY PRIMARY KEY, FIRST_NAME VARCHAR, LAST_NAME VARCHAR, AGE INTEGER, MAGAZINE INTEGER, MAGAZINE_INDEX INTEGER, FOREIGN KEY (MAGAZINE) REFERENCES MAGAZINE (ID) )
雑誌のテーブルは何も変わっていません.
モデルのテーブルは,名前を姓と名に分けて,外部キー制約を指定しました.さらに,雑誌への関連をリスト (順序付き) にするために,MAGAZINE_INDEX カラムを追加しています.
そんなわけで (どんなわけで?),新しい雑誌の永続クラス.
package study; import java.util.ArrayList; import java.util.List; public class Magazine { int id = -1; String name; String publisher; List model; public Magazine() { } public Magazine(String name, String publisher) { this.name = name; this.publisher = publisher; this.model = new ArrayList(); } public String toString() { return name + "(" + publisher + "):" + model; } }
モデルへの関連を Set
から List
に変更しています.
そして雑誌のマッピングファイル.
<?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" default-access="field"> <class name="Magazine"> <id name="id" unsaved-value="-1"> <generator class="identity"/> </id> <property name="name"/> <property name="publisher"/> <list name="model" cascade="save-update"> <key column="magazine"/> <index column="magazine_index"/> <one-to-many class="Model"/> </list> </class> </hibernate-mapping>
続いてモデルの永続クラス.
package study; public class Model { int id = -1; Name name; int age; Magazine magazine; public Model() { } public Model(String firstName, String lastName, int age, Magazine magazine) { this.name = new Name(firstName, lastName); this.age = age; this.magazine = magazine; } public String toString() { return name + "(" + age + ")"; } }
雑誌への関連を持つようになりました.
名前は姓と名でカラムが分かれたため,Name
というクラスを使っています.
その名前クラス.
package study; public class Name { String firstName; String lastName; public Name() { } public Name(String firstName, String lastName) { this.firstName = firstName; this.lastName = lastName; } public String toString() { return firstName + " " + lastName; } }
そしてモデルのマッピングファイル.
<?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" default-access="field"> <class name="Model"> <id name="id" unsaved-value="-1"> <generator class="identity"/> </id> <component name="name" class="study.Name"> <property name="firstName" column="FIRST_NAME"/> <property name="lastName" column="LAST_NAME"/> </component> <property name="age"/> <many-to-one name="magazine" class="study.Magazine"/> </class> </hibernate-mapping>
姓と名の二つのカラムを name
というプロパティにマッピングしています.
これでコンポーネントが必要な例も,1対多関連が必要な例も試せます.
そしてデータ作成用のクラス.
package study; import net.sf.hibernate.Session; public class Save { public static void main(String[] args) { try { HibernateTemplate template = new HibernateTemplate(); template.process(new HibernateCallback() { public Object doProcess(Session session) throws Exception { Magazine cancam = new Magazine("CanCam", "Shogakukan"); session.save(cancam); Magazine oggi = new Magazine("Oggi", "Shogakukan"); session.save(oggi); Magazine jj = new Magazine("JJ", "Kobunsha"); session.save(jj); Magazine vivi = new Magazine("ViVi", "Kodansha"); session.save(vivi); Magazine ray = new Magazine("Ray", "Shufunotomo"); session.save(ray); cancam.model.add(new Model("Yuri", "Ebihara", 24, cancam)); cancam.model.add(new Model("Asami", "Usuda", 19, cancam)); cancam.model.add(new Model("Yu", "Yamada", 20, cancam)); oggi.model.add(new Model("Satoko", "Koizumi", 23, oggi)); vivi.model.add(new Model("Jun", "Hasegawa", 18, vivi)); ray.model.add(new Model("Karina", null, 20, ray)); session.save(new Model("Sayo", "Aizawa", 26, null)); return null; } }); } catch (Exception e) { e.printStackTrace(); } } }
前々回は Insert
という名前でしたが,今回は少し Hibernate っぽく Save
にしてみました.どうでもいい? 残念!!!!
それから,今回は 元 CanCam モデルで現在は Oggi で活躍中の里子ちゃん初登場です.TV CM にも出てますね.どうでもいい? 残念!!!!
こいつを実行しておきます.
問い合せ用のクラスは前々回と同じです.
ということで,前回試せなかった例を一つやってみましょう.
FROM 句で結合を指定しなくても,WHERE 句で関連を辿ると勝手に結合されるというやつ.
from study.Model model where model.magazine.publisher = 'Shogakukan'
その実行結果.
Yuri Ebihara(24) Asami Usuda(19) Yu Yamada(20) Satoko Koizumi(23)
CanCam モデル 3 人に加えて,同じく小学館発行の Oggi から里子ちゃんも引っかかりました.
このとき,実際に実行された SQL は...
select model0_.id as id, model0_.FIRST_NAME as FIRST_NAME, model0_.LAST_NAME as LAST_NAME, model0_.age as age, model0_.magazine as magazine from Model model0_, Magazine magazine1_ where (magazine1_.publisher='Shogakukan' and model0_.magazine=magazine1_.id)
と,きっちり結合されています.
では,そろそろ今日の学習を始めましょう.前振り長すぎ? 残念!!!!
そんなわけで (どんなわけで?),「11.8. Expressions」です.
HQL で扱うことの出来る式がずらずら〜っと.これは辛いなぁ... とりあえず一覧.
+,-,*,/
=,>=,<=,><,!=,like
and,or,not
||
upper(),lower()
( )
in,between,is null
?,:name
- リテラル
なんか,どれもどってことない感じ.そんなわけで (どんなわけで?),スキップ,スキップ♪
次に出てくるのは 真偽値の扱い.HQL 中で true
や false
を使った場合に,それを別の値に置き換えることが出来るようです.
そのためには,hibernate.cfg.xml
にプロパティを次のように記述します.
<property name="hibernate.query.substitutions">true 1, false 0</property>
すると,true
は 1 に,false
は 0 に置き換えられます.
ということで次の問い合せ
from study.Model model where model.id = true
id
は boolean
ではなくて int
なので意味はないのですが,ともかくこれを実行!!!!
Yuri Ebihara(24)
id
が 1 なのはエビちゃんなんですね.
使っている RDBMS が BOOLEAN なデータ型をサポートしていない場合に重宝しそうです.
次はコレクションに関連する話題.
コレクションのサイズを WHERE 句に含めることが出来るそうです.
from study.Magazine magazine where size(magazine.model) > 1
こいつを実行!!
CanCam(Shogakukan):[Yuri Ebihara(24), Asami Usuda(19), Yu Yamada(20)]
専属モデルを二人以上登録してあるのは CanCam だけです.ばっちり.
この時に実行された SQL は次の通り.
select magazine0_.id as id, magazine0_.name as name, magazine0_.publisher as publisher from Magazine magazine0_ where ((select count(*) from Model model1_ where magazine0_.id=model1_.magazine) > 1)
勝手に副問い合せをしてくれるのですね.
続いては,順序付きコレクション (リストや配列) の先頭の要素を参照する minElement
と最後の要素を参照する maxElement
です.これのために雑誌とモデルの関連をリストにしたのです♪
そんなわけで (どんなわけで?),次の問い合せ.
select model from study.Magazine magazine inner join magazine.model model where model = magazine.model.minElement
各雑誌に関連づけられた,最初のモデルだけを取ってこようという問い合せ.随分回りくどいやり方という気もしますが,ここは minElement
を使うことに意義があるのです.
その実行結果.
Yuri Ebihara(24) Satoko Koizumi(23) Jun Hasegawa(18) Karina null(20)
おおぉぉぉっ,うまくできたっぽい.ちなみに CanCam 以外はモデルは一人しかいないのであまりおもしろくありません.
その時の SQL.
select model1_.id as id, model1_.FIRST_NAME as FIRST_NAME, model1_.LAST_NAME as LAST_NAME, model1_.age as age, model1_.magazine as magazine from Magazine magazine0_ inner join Model model1_ on magazine0_.id=model1_.magazine where (model1_.id = (select min(id) from Model model2_ where magazine0_.id=model2_.magazine))
なるほど.
minElement
および maxElement
は,プロパティ風の使い方だけでなく,関数風に使うことも出来るそうです.
そんなわけで (どんなわけで?),関数風な呼び出しで最後のモデルだけを取得.
select model from study.Magazine magazine inner join magazine.model model where model = maxElemtn(magazine.model)
その問い合せ結果.
Yu Yamada(20) Satoko Koizumi(23) Jun Hasegawa(18) Karina null(20)
ふむ.
コレクションの最初の要素または最後の要素のインデックスを取得する,minIndex
および maxIndex
というのも用意されているようです.
from study.Magazine magazine where magazine.model.maxIndex > 0
最後の要素のインデックスが 0 を超えている,つまり二人以上のモデルを抱えている雑誌のみを問い合せます.
その実行結果.
CanCam(Shogakukan):[Yuri Ebihara(24), Asami Usuda(19), Yu Yamada(20)]
ふむふむ.
その時の SQL.
select magazine0_.id as id, magazine0_.name as name, magazine0_.publisher as publisher from Magazine magazine0_ where ((select max(magazine_index) from Model model1_ where magazine0_.id=model1_.magazine) > 0)
ふむふむふむ.
次は any, some, all, exists, in などなど.ふーっ,そろそろお疲れ気味.
こいつらを使うには,コレクションに対して elements
や indices
という関数を適用するようです.
まずは簡単なものから.
from study.Magazine magazine where exists elements(magazine.model)
専属モデルが存在する雑誌を問い合せています.
その実行結果.
CanCam(Shogakukan):[Yuri Ebihara(24), Asami Usuda(19), Yu Yamada(20)] Oggi(Shogakukan):[Satoko Koizumi(23)] ViVi(Kodansha):[Jun Hasegawa(18)] Ray(Shufunotomo):[Karina null(20)]
専属モデルを登録していない JJ が表示されていません.ばっちり.
その時の SQL.
select magazine0_.id as id, magazine0_.name as name, magazine0_.publisher as publisher from Magazine magazine0_ where (exists(select model1_.id from Model model1_ where magazine0_.id = model1_.magazine))
もう一つ.
from study.Magazine magazine where 2 in indices(magazine.model)
indices(magazine.model)
は,雑誌が持っている順序付きコレクションのインデックスの集合です.
その実行結果.
CanCam(Shogakukan):[Yuri Ebihara(24), Asami Usuda(19), Yu Yamada(20)]
インデックスに 2 を含んでいるのは CanCam だけです.その他の雑誌は高々モデル一人しかいないので,インデックスは (あっても) 0 しかありません.
その時の SQL
select magazine0_.id as id, magazine0_.name as name, magazine0_.publisher as publisher from Magazine magazine0_ where (2 in (select model1_.magazine_index from Model model1_ where magazine0_.id = model1_.magazine))
この例は,マップを使った方がおもしろかったですね.残念!!!!
これら size, elements, indices, minIndex, maxIndex, minElement, maxElement
には,次の制限があるとのこと.
- WHERE 句で使えるのは,RDBMS が副問い合せをサポートしている場合のみ.
- SELECT 句で使えるのは,
elements
とindices
のみ.
ふーん,SELECT 句でも使えるんだぁ.
ということでやってみました.
select elements(magazine.model) from study.Magazine magazine
その実行結果.
Yuri Ebihara(24) Asami Usuda(19) Yu Yamada(20) Satoko Koizumi(23) Jun Hasegawa(18) Karina null(20)
ほほぉ.
その時の SQL
select model2_.id as x0_0_ from Magazine magazine0_, Model model1_, Model model2_ where magazine0_.id = model1_.magazine and model1_.id = model2_.id
むむぅ?
なぜにモデルを2回も結合しているのでしょうか?
model1_
はなんの役にも立っていないような?
ちょっと気になりますね...
ともあれ (JW),次へ行きましょう.
インデックス付きのコレクションすなわち,リスト,配列,マップの場合,WHERE 句ではそのインデックスを指定できるそうです.
from study.Magazine magazine where magazine.model[0].age > 20
その実行結果.
CanCam(Shogakukan):[Yuri Ebihara(24), Asami Usuda(19), Yu Yamada(20)] Oggi(Shogakukan):[Satoko Koizumi(23)]
その時の SQL
select magazine0_.id as id, magazine0_.name as name, magazine0_.publisher as publisher from Magazine magazine0_, Model model1_ where (model1_.age > 20 and magazine0_.id = model1_.magazine and model1_.magazine_index = 0)
ふむ.
[]
の内側には,式も記述できるそうです.
from study.Magazine magazine where magazine.model[maxIndex(magazine.model)].age <= 20
その実行結果.
CanCam(Shogakukan):[Yuri Ebihara(24), Asami Usuda(19), Yu Yamada(20)] ViVi(Kodansha):[Jun Hasegawa(18)] Ray(Shufunotomo):[Karina null(20)]
その時のSQL
select magazine0_.id as id, magazine0_.name as name, magazine0_.publisher as publisher from Magazine magazine0_, Model model1_ where (model1_.age <= 20 and magazine0_.id = model1_.magazine and model1_.magazine_index = (select max(magazine_index) from Model model2_ where magazine0_.id=model2_.magazine))
ふむふむ.
さらに,1対多関連の場合,index()
という関数を使うことで,インデックスを得ることが出来るそうです.
select index(model), model from study.Magazine magazine inner join magazine.model model
その実行結果.
[0, Yuri Ebihara(24)] [1, Asami Usuda(19)] [2, Yu Yamada(20)] [0, Satoko Koizumi(23)] [0, Jun Hasegawa(18)] [0, Karina null(20)]
その時のSQL
select model1_.id as id, model1_.FIRST_NAME as FIRST_NAME, model1_.LAST_NAME as LAST_NAME, model1_.age as age, model1_.magazine as magazine, model1_.magazine_index as x0_0_, model1_.id as x1_0_ from Magazine magazine0_ inner join Model model1_ on magazine0_.id = model1_.magazine
ふむふむふむ.
最後は単調に試しただけになってしまいましたが,一応「11.8. Expressions」終了です.
ちょっと雑すぎ? でも,文字数だけは多いんです.なので,
『はんぶんにしてくださぁぁぁぁぁい!!!!!』©眞鍋かをり
と思った人がきっといるはずだ.