HQL: The Hibernate Query Language
HibernateはSQLに非常によく似た(意図的に似せた)強力な問い合わせ言語を備えています。
しかしSQLに似た構文に惑わされないでください。HQLは完全にオブジェクト指向であり、
継承、ポリモーフィズム、関連といった概念を理解します。
大文字と小文字の区別
クエリはJavaのクラス名とプロパティ名を除いて大文字、小文字を区別しません。
従って SeLeCT は
sELEct と同じで、かつ
SELECT とも同じですが
net.sf.hibernate.eg.FOO は
net.sf.hibernate.eg.Foo とは違い、かつ
foo.barSet は
foo.BARSET とも違います。
このマニュアルでは小文字のHQLキーワードを使用します。
大文字のキーワードのクエリの方が読みやすいと感じるユーザーもいると思います。
ですが、Javaコード内に埋め込まれたときには見づらいと思います。
from節
もっとも単純なHibernateクエリは次の形式です。
これは単純に eg.Cat クラスのインスタンスをすべて返します。
必ずしもクラス名を修飾する(クラスにパッケージ名を付ける)必要はありません。
というのも、auto-import がデフォルトになっているからです。
そのためほとんどの場合、このように書くだけで十分です。
ほとんどの場合クエリのほかの部分で Cat を参照するので、別名を割り当てる必要があるでしょう。
このクエリでは Cat インスタンスに cat
という別名を付けています。
そのため、後でこのクエリ内で、この別名を使うことができます。
as キーワードはオプションです。つまりこのように書くこともできます:
直積、あるいはクロス結合によって多数のクラスが出現することもあります。
ローカル変数のJavaのネーミング基準と一致した、
頭文字に小文字を使ったクエリの別名を付けることはいい習慣です(例えば domesticCat )。
関連と結合
関連するエンティティあるいは値コレクションの要素にも、結合 を使って別名を割り当てることが出来ます。
サポートしている結合のタイプはANSI SQLと同じです。
inner join
left outer join
right outer join
full join (たいていの場合使いづらい)
inner join、left outer join、right outer joinには省略形を使うこともできます。
HQLの with キーワードを使うと、結合条件を付け加えることができます。
10.0]]>
加えて、「フェッチ」結合は関連や値のコレクションを親オブジェクトと一緒に1度のselect句で初期化します。
これは特にコレクションの場合に有用です。これは実質上、関連とコレクションに対するマッピング定義ファイルの外部結合とlazy初期化の定義を上書きすることになります。
により多くの情報があります。
結合によるフェッチは関連するオブジェクトが where 節(または他のどんな節でも)
で使われてはならないので、通常別名を割り当てる必要がありません。また関連オブジェクトは問い合わせ結果として
直接返されません。代わりに親オブジェクトを通してアクセスできます。
コレクションを再帰的に結合フェッチする場合のみ、別名が必要になります。
fetch 構文は iterate()
を使ったクエリ呼び出しで使用できないことに注意してください
(一方で scroll() は使用できます)。
また、これらの操作は結果の行に基づいているため、
fetch は setMaxResults() や setFirstResult()
と一緒に使用すべきではありません。
通常eagerなコレクションフェッチをすると重複が出てしまうため、あなたが期待するような行数にはならないのです。
そしてまた fetch は、アドホックな with 条件を
一緒に使うこともできません。
一つのクエリで複数のコレクションを結合フェッチすることにより直積を作成できるので、この場合注意してください。
また、複数のコレクションに対する結合フェッチはbagマッピングに対して予期せぬ結果をもたらすことがあるので、
この場合のクエリの作成には注意してください。
最後に 全外部結合によるフェッチ と
右外部結合によるフェッチ は有用ではないことに注意してください。
もしプロパティレベルの遅延フェッチを使う場合(内部的にバイトコード処理をする場合)、
fetch all properties を使うことで
Hibernateに遅延プロパティを速やかに(最初のクエリで)フェッチさせることができます。
結合構文の形式
HQLは2つの関連結合形式をサポートします:暗黙的 と 明示的。
これまでのセクションでお見せした使い方はすべて 明示的な 形式で、
from節で明示的にjoinキーワードを使っています。
この形式をおすすめします。
暗黙的 フォームは、joinキーワードを使いません。代わりに、参照する関連に
ドット表記を使います。暗黙的 結合は、さまざまなHQLに出てきます。
暗黙的 結合の結果は、SQLステートメントの内部結合結果です。
Select節
select 節は以下のようにどのオブジェクトと属性をクエリリザルトセットに返すかを選択します。:
上記のクエリは他の Cat の mate を選択します。
実際には次のように、より簡潔に表現できます。:
クエリはコンポーネント型のプロパティを含む、あらゆる値型のプロパティも返せます。:
クエリは複数のオブジェクトと(または)プロパティを Object[] 型の配列として返せます。
もしくは List として、
または、タイプセーフなJavaオブジェクトを返せます。
あるいは Family クラスが適切なコンストラクタを持っているとするならば、
select節に as を使って別名をつけることもできます。
select new map と一緒に使うときに最も役立ちます:
このクエリは別名からselectした値へ Map を返します。
集約関数
HQLのクエリはプロパティの集約関数の結果も返せます:
サポートしている集約関数は以下のものです。
avg(...), sum(...), min(...), max(...)
count(*)
count(...), count(distinct ...), count(all...)
select節において算術操作、連結と承認されたSQL関数を使うことができます。
SQLと同じ意味を持つ distinct と all キーワードを使うことができます。
ポリモーフィックなクエリ
次のようなクエリ:
Cat インスタンスだけではなく、DomesticCat
のようなサブクラスも返されます。Hibernateクエリは どんな
Javaクラスやインターフェイスも from 節に入れることができます。
クエリはそのクラスを拡張した、もしくはインターフェイスを実装した全ての永続クラスを返します。
次のクエリは永続オブジェクトをすべて返します:
Named インターフェイスは様々な永続クラスによって実装されます。:
最後の二つのクエリは、二つ以上のSQL SELECT を要求していることに注意してください。
このことは order by 節がリザルトセット全体を正確には整列しないことを意味します
(さらにそれは、Query.scroll() を使用してこれらのクエリを呼ぶことができないことを意味します。)。
where節
where 節は返されるインスタンスのリストを絞ることができます。
もし別名がない場合、名前でプロパティを参照します。
もし別名がある場合、修飾名を使ってください。
名前が'Fritz'という Cat のインスタンスを返します。
上のHQLは、Foo の startDate プロパティと等しい
date プロパティを持った bar インスタンスが存在する、
すべての Foo インスタンスを返します。
コンパウンドパス式(例えば「cat.mate.name」)は where 節を非常に強力にします。注目:
このクエリはテーブル結合(内部結合)を持つSQLクエリに変換されます。
その代わりに以下のように書くと、
もし上のクエリを記述したらクエリ内に4つのテーブル結合を必要とするSQLクエリに変換されます。
= 演算子は以下のように、プロパティだけでなくインスタンスを比較するためにも使われます。:
id (小文字)は特別なプロパティであり、
オブジェクトのユニークな識別子を参照するために使用できます。(さらに、そのプロパティ名を使用できます。)
2番目のクエリは効率的です。テーブル結合が必要ありません!
また複合識別子のプロパティも使用できます。ここで Person が
country と medicareNumber からなる複合識別子を持つと仮定します。
もう一度言いますが、2番目のクエリにはテーブル結合が必要ありません。
同様に class は特別なプロパティであり、
ポリモーフィックな永続化におけるインスタンスのdiscriminator値にアクセスします。
where節に埋め込まれたJavaのクラス名はそのdiscriminator値に変換されます。
またコンポーネントや複合ユーザ型(またそのコンポーネントのコンポーネントなど)のプロパティも指定できます。
しかし決して(コンポーネントのプロパティではなく)コンポーネント型のプロパティで終了するパス式を使わないでください。
例えばもし store.owner が address
コンポーネントを持つエンティティならば以下のような結果となります。
"any"型は特別なプロパティである id と class を持ち、
以下の方法で結合を表現することを可能にします(AuditLog.item は
<any> でマッピングされたプロパティです)。
log.item.class と payment.class が
上記のクエリ中で全く異なるデータベースカラムの値を参照するということに注意してください。
Expressions 式
SQLの where 節で記述することが出来る式のほとんどをHQLでも記述できます。:
算術演算子:+, -, *, /
2項比較演算子:=, >=, <=, <>, !=, like
論理演算子:and, or, not
グループ分けを表す括弧:( )
in,
not in,
between,
is null,
is not null,
is empty,
is not empty,
member of and
not member of
"シンプル"なcase case ... when ... then ... else ... end、
"探索的"なcase case when ... then ... else ... end
ストリングの連結 ...||... または concat(...,...)
current_date(), current_time(),
current_timestamp()
second(...), minute(...),
hour(...), day(...),
month(...), year(...),
EJB-QL 3.0で定義されている関数や演算子: substring(), trim(),
lower(), upper(), length(), locate(), abs(), sqrt(), bit_length()
coalesce() と nullif()
数字や時間の値をStringにコンバートする str())
2番目の引数がHibernate型の名前である cast(... as ...) と
extract(... from ...)。
ただし使用するデータベースがANSI cast() と extract()
をサポートする場合に限ります。
結合したインデックス付きのコレクションの別名に適用されるHQLの
index() 関数。
コレクション値のパス式を取るHQL関数: size(), minelement(), maxelement(),
minindex(), maxindex() 。
some, all, exists, any, in を使って修飾することができる特別な
elements() と indices 関数と一緒に使います。
sign(), trunc(), rtrim(), sin()
のようなデータベースがサポートするSQLスカラ関数。
JDBCスタイルの位置パラメータ ?
名前付きパラメータ: :name, :start_date, :x1
SQLリテラル:'foo', 69, 6.66E+2,
'1970-01-01 10:00:01.0'
Javaの public static final 定数:eg.Color.TABBY
in と between は以下のように使用できます。:
また、否定形で記述することもできます。
同様に is null や is not null はnull値をテストするために使用できます。
Hibernate設定ファイルでHQL query substitutionsを定義すれば、boolean値を式の中で簡単に使用できま。:
true 1, false 0]]>
こうすることで下記のHQLをSQLに変換するときに true ,
false キーワードは 1 , 0 に置き換えられます。:
特別なプロパティ size、または特別な関数 size()
を使ってコレクションのサイズをテストできます。:
0]]>
0]]>
インデックス付きのコレクションでは、minindex と maxindex
関数を使って、インデックスの最小値と最大値を参照できます。
同様に、minelement と maxelement を使って、
基本型のコレクション要素の最小値と最大値を参照できます。
current_date]]>
100]]>
10000]]>
コレクションの要素やインデックスのセット(elements と indices
関数)、または副問い合わせ(後述)の結果が受け取れるときは、
SQL関数 any, some, all, exists, in がサポートされます。
all elements(p.scores)]]>
size, elements, indices,
minindex, maxindex, minelement,
maxelement はHibernate3のwhere節だけで利用可能であることに注意してください。
インデックス付きのコレクション(arrays, lists, maps)の要素は、
インデックスで参照できます(where節内でのみ)。
[] 内部の式は、算術式でも構いません。
一対多関連や値のコレクションの要素に対しては、HQLは組み込みの index() 関数も用意しています。
ベースとなるデータベースがサポートしているスカラーSQL関数が使用できます
もしまだ全てを理解していないなら、下のクエリをSQLでどれだけ長く、読みづらく出来るか考えてください。:
ヒント: 例えばこのように出来ます。
order by節
クエリが返すlistは、返されるクラスやコンポーネントの任意の属性によって並べ替えられます。:
オプションの asc と desc はそれぞれ昇順か降順の整列を示します。
group by節
集約値を返すクエリは、返されるクラスやコンポーネントの任意のプロパティによってグループ化できます。:
having 節も使えます。
もし使用するデータベースがサポートしているなら、
having と order by 節でSQL関数と集約関数が使えます
(例えばMySQLにはありません)。
100
order by count(kitten) asc, sum(kitten.weight) desc]]>
group by 節や order by 節に
算術式を含むことができないことに注意してください。
副問い合わせ
サブセレクトをサポートするデータベースのため、Hibernateは副問い合わせをサポートしています。
副問い合わせは括弧で囲まなければなりません(SQLの集約関数呼び出しによる事が多いです)。
関連副問い合わせ(外部クエリ中の別名を参照する副問い合わせのこと)さえ許可されます。
(
select avg(cat.weight) from DomesticCat cat
)]]>
HQL副問い合わせは、selectまたはwhere節だけで使われることに注意してください。
selectリストに複数の式を持つ副問い合わせには、タプルを使うことができます。
いくつかのデータベース(OracleやHSQLにはありません)では、
他のコンテキストでもタプルが使えます。
例えば、クエリコンポーネントや複合ユーザ型においてです。
同等ですが、より冗長なクエリです:
このようなことをしたくないのには2つの理由があります:
1つ目は、データベースプラットフォーム間で完全な互換性はないからです。
2つ目は、クエリがマッピングドキュメントのプロパティの順序に依存するからです。
HQLの例
Hibernateクエリは非常に強力で複雑にできます。実際、クエリ言語の威力はHibernateの主要なセールスポイントの一つです。
ここに最近のプロジェクトで使用したクエリと非常によく似た例があります。
ほとんどのクエリはこれらの例より簡単に記述できることに注意してください!
以下のクエリは特定の顧客と与えられた最小の合計値に対する未払い注文の注文ID、
商品の数、注文の合計を合計値で整列して返します。
価格を決定する際、現在のカタログを使います。結果として返されるSQLクエリは
ORDER, ORDER_LINE, PRODUCT,
CATALOG および PRICE テーブルに対し4つの内部結合と
(関連しない)副問い合わせを持ちます。
= all (
select cat.effectiveDate
from Catalog as cat
where cat.effectiveDate < sysdate
)
group by order
having sum(price.amount) > :minAmount
order by sum(price.amount) desc]]>
何て巨大なクエリなのでしょう! 普段私は副問い合わせをあまり使いません。したがって私のクエリは実際には以下のようになります。:
:minAmount
order by sum(price.amount) desc]]>
次のクエリは各ステータスの支払い数を数えます。ただしすべての支払いが現在の利用者による
最新のステータス変更である AWAITING_APPROVAL である場合を除きます。
このクエリは2つの内部結合と PAYMENT, PAYMENT_STATUS および
PAYMENT_STATUS_CHANGE テーブルに対する関連副問い合わせを備えたSQLクエリに変換されます。
PaymentStatus.AWAITING_APPROVAL
or (
statusChange.timeStamp = (
select max(change.timeStamp)
from PaymentStatusChange change
where change.payment = payment
)
and statusChange.user <> :currentUser
)
group by status.name, status.sortOrder
order by status.sortOrder]]>
もし私がsetの代わりにlistとして statusChanges コレクションを
マッピングしたならば、はるかに簡単にクエリを記述できるでしょう。
PaymentStatus.AWAITING_APPROVAL
or payment.statusChanges[ maxIndex(payment.statusChanges) ].user <> :currentUser
group by status.name, status.sortOrder
order by status.sortOrder]]>
次のクエリは現在のユーザが所属する組織に対するアカウントおよび未払いの支払いを
すべて返すMS SQL Server
の isNull() 関数を使用しています。
このクエリは3つの内部結合と1つの外部結合 、
そして ACCOUNT, PAYMENT, PAYMENT_STATUS,
ACCOUNT_TYPE, ORGANIZATION および
ORG_USER テーブルに対する副問い合わせ持ったSQLに変換されます。
いくつかのデータベースについては、(関連させられた)副問い合わせの使用を避ける必要があるでしょう。
大量のUPDATEとDELETE
HQLは今は update と delete、insert ... select ...
ステートメントをHQLに入れることをサポートしています。
に詳細があります。
Tips & Tricks
実際に結果を返さなくてもクエリの結果数を数えることができます。:
コレクションのサイズにより結果を並べ替えるためには以下のクエリを使用します。:
使用しているデータベースがサブセレクトをサポートする場合、クエリのwhere節でサイズによる選択条件を設定できます:
= 1]]>
使用しているデータベースがサブセレクトをサポートしない場合は、次のクエリを使用してください:
= 1]]>
内部結合をしているせいで上の解決法がmessageの件数が
ゼロの User を返すことができないならば、以下の形式が使えます。
JavaBeanのプロパティは、名前付きのクエリパラメータに結びつけることが出来ます。:
コレクションはフィルタ付き Query インターフェイスを使用することでページをつけることができます。:
コレクションの要素はクエリフィルタを使って、並べ替えやグループ分けが出来ます。:
コレクションを初期化せずにコレクションのサイズを得ることができます。: