hibernate-orm/doc/reference/ja/modules/query_hql.xml

1132 lines
45 KiB
XML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?xml version="1.0" encoding="Shift_JIS"?>
<chapter id="queryhql">
<title>HQL: The Hibernate Query Language</title>
<para>
HibernateはSQLに非常によく似た(意図的に似せた)強力な問い合わせ言語を備えています。
しかしSQLに似た構文に惑わされないでください。HQLは完全にオブジェクト指向であり、
継承、ポリモーフィズム、関連といった概念を理解します。
</para>
<sect1 id="queryhql-casesensitivity">
<title>大文字と小文字の区別</title>
<para>
クエリはJavaのクラス名とプロパティ名を除いて大文字、小文字を区別しません。
従って <literal>SeLeCT</literal>
<literal>sELEct</literal> と同じで、かつ
<literal>SELECT</literal> とも同じですが
<literal>net.sf.hibernate.eg.FOO</literal>
<literal>net.sf.hibernate.eg.Foo</literal> とは違い、かつ
<literal>foo.barSet</literal>
<literal>foo.BARSET</literal> とも違います。
</para>
<para>
このマニュアルでは小文字のHQLキーワードを使用します。
大文字のキーワードのクエリの方が読みやすいと感じるユーザーもいると思います。
ですが、Javaコード内に埋め込まれたときには見づらいと思います。
</para>
</sect1>
<sect1 id="queryhql-from">
<title>from節</title>
<para>
もっとも単純なHibernateクエリは次の形式です。
</para>
<programlisting><![CDATA[from eg.Cat]]></programlisting>
<para>
これは単純に <literal>eg.Cat</literal> クラスのインスタンスをすべて返します。
必ずしもクラス名を修飾する(クラスにパッケージ名を付ける)必要はありません。
というのも、<literal>auto-import</literal> がデフォルトになっているからです。
そのためほとんどの場合、このように書くだけで十分です。
</para>
<programlisting><![CDATA[from Cat]]></programlisting>
<para>
ほとんどの場合クエリのほかの部分で <literal>Cat</literal> を参照するので、別名を割り当てる必要があるでしょう。
</para>
<programlisting><![CDATA[from Cat as cat]]></programlisting>
<para>
このクエリでは <literal>Cat</literal> インスタンスに <literal>cat</literal>
という別名を付けています。
そのため、後でこのクエリ内で、この別名を使うことができます。
<literal>as</literal> キーワードはオプションです。つまりこのように書くこともできます:
</para>
<programlisting><![CDATA[from Cat cat]]></programlisting>
<para>
直積、あるいはクロス結合によって多数のクラスが出現することもあります。
</para>
<programlisting><![CDATA[from Formula, Parameter]]></programlisting>
<programlisting><![CDATA[from Formula as form, Parameter as param]]></programlisting>
<para>
ローカル変数のJavaのネーミング基準と一致した、
頭文字に小文字を使ったクエリの別名を付けることはいい習慣です(例えば <literal>domesticCat</literal> )。
</para>
</sect1>
<sect1 id="queryhql-joins" revision="2">
<title>関連と結合</title>
<para>
関連するエンティティあるいは値コレクションの要素にも、<literal>結合</literal> を使って別名を割り当てることが出来ます。
</para>
<programlisting><![CDATA[from Cat as cat
inner join cat.mate as mate
left outer join cat.kittens as kitten]]></programlisting>
<programlisting><![CDATA[from Cat as cat left join cat.mate.kittens as kittens]]></programlisting>
<programlisting><![CDATA[from Formula form full join form.parameter param]]></programlisting>
<para>
サポートしている結合のタイプはANSI SQLと同じです。
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
<literal>inner join</literal>
</para>
</listitem>
<listitem>
<para>
<literal>left outer join</literal>
</para>
</listitem>
<listitem>
<para>
<literal>right outer join</literal>
</para>
</listitem>
<listitem>
<para>
<literal>full join</literal> (たいていの場合使いづらい)
</para>
</listitem>
</itemizedlist>
<para>
<literal>inner join</literal><literal>left outer join</literal><literal>right outer join</literal>には省略形を使うこともできます。
</para>
<programlisting><![CDATA[from Cat as cat
join cat.mate as mate
left join cat.kittens as kitten]]></programlisting>
<para>
HQLの <literal>with</literal> キーワードを使うと、結合条件を付け加えることができます。
</para>
<programlisting><![CDATA[from Cat as cat
left join cat.kittens as kitten
with kitten.bodyWeight > 10.0]]></programlisting>
<para>
加えて、「フェッチ」結合は関連や値のコレクションを親オブジェクトと一緒に1度のselect句で初期化します。
これは特にコレクションの場合に有用です。これは実質上、関連とコレクションに対するマッピング定義ファイルの外部結合とlazy初期化の定義を上書きすることになります。
<xref linkend="performance-fetching"/> により多くの情報があります。
</para>
<programlisting><![CDATA[from Cat as cat
inner join fetch cat.mate
left join fetch cat.kittens]]></programlisting>
<para>
結合によるフェッチは関連するオブジェクトが <literal>where</literal> 節(または他のどんな節でも)
で使われてはならないので、通常別名を割り当てる必要がありません。また関連オブジェクトは問い合わせ結果として
直接返されません。代わりに親オブジェクトを通してアクセスできます。
コレクションを再帰的に結合フェッチする場合のみ、別名が必要になります。
</para>
<programlisting><![CDATA[from Cat as cat
inner join fetch cat.mate
left join fetch cat.kittens child
left join fetch child.kittens]]></programlisting>
<para>
<literal>fetch</literal> 構文は <literal>iterate()</literal>
を使ったクエリ呼び出しで使用できないことに注意してください
(一方で <literal>scroll()</literal> は使用できます)。
また、これらの操作は結果の行に基づいているため、
<literal>fetch</literal><literal>setMaxResults()</literal><literal>setFirstResult()</literal>
と一緒に使用すべきではありません。
通常eagerなコレクションフェッチをすると重複が出てしまうため、あなたが期待するような行数にはならないのです。
そしてまた <literal>fetch</literal> は、アドホックな <literal>with</literal> 条件を
一緒に使うこともできません。
一つのクエリで複数のコレクションを結合フェッチすることにより直積を作成できるので、この場合注意してください。
また、複数のコレクションに対する結合フェッチはbagマッピングに対して予期せぬ結果をもたらすことがあるので、
この場合のクエリの作成には注意してください。
最後に <literal>全外部結合によるフェッチ</literal>
<literal>右外部結合によるフェッチ</literal> は有用ではないことに注意してください。
</para>
<para>
もしプロパティレベルの遅延フェッチを使う場合(内部的にバイトコード処理をする場合)、
<literal>fetch all properties</literal> を使うことで
Hibernateに遅延プロパティを速やかに最初のクエリでフェッチさせることができます。
</para>
<programlisting><![CDATA[from Document fetch all properties order by name]]></programlisting>
<programlisting><![CDATA[from Document doc fetch all properties where lower(doc.name) like '%cats%']]></programlisting>
</sect1>
<sect1 id="queryhql-joins-forms">
<title>結合構文の形式</title>
<para>
HQLはつの関連結合形式をサポートします<literal>暗黙的</literal><literal>明示的</literal>
</para>
<para>
これまでのセクションでお見せした使い方はすべて <literal>明示的な</literal> 形式で、
from節で明示的にjoinキーワードを使っています。
この形式をおすすめします。
</para>
<para>
<literal>暗黙的</literal> フォームは、joinキーワードを使いません。代わりに、参照する関連に
ドット表記を使います。<literal>暗黙的</literal> 結合は、さまざまなHQLに出てきます。
<literal>暗黙的</literal> 結合の結果は、SQLステートメントの内部結合結果です。
</para>
<programlisting><![CDATA[from Cat as cat where cat.mate.name like '%s%']]></programlisting>
</sect1>
<sect1 id="queryhql-select">
<title>Select節</title>
<para>
<literal>select</literal> 節は以下のようにどのオブジェクトと属性をクエリリザルトセットに返すかを選択します。:
</para>
<programlisting><![CDATA[select mate
from Cat as cat
inner join cat.mate as mate]]></programlisting>
<para>
上記のクエリは他の <literal>Cat</literal><literal>mate</literal> を選択します。
実際には次のように、より簡潔に表現できます。:
</para>
<programlisting><![CDATA[select cat.mate from Cat cat]]></programlisting>
<para>
クエリはコンポーネント型のプロパティを含む、あらゆる値型のプロパティも返せます。:
</para>
<programlisting><![CDATA[select cat.name from DomesticCat cat
where cat.name like 'fri%']]></programlisting>
<programlisting><![CDATA[select cust.name.firstName from Customer as cust]]></programlisting>
<para>
クエリは複数のオブジェクトと(または)プロパティを <literal>Object[]</literal> 型の配列として返せます。
</para>
<programlisting><![CDATA[select mother, offspr, mate.name
from DomesticCat as mother
inner join mother.mate as mate
left outer join mother.kittens as offspr]]></programlisting>
<para>
もしくは <literal>List</literal> として、
</para>
<programlisting><![CDATA[select new list(mother, offspr, mate.name)
from DomesticCat as mother
inner join mother.mate as mate
left outer join mother.kittens as offspr]]></programlisting>
<para>
または、タイプセーフなJavaオブジェクトを返せます。
</para>
<programlisting><![CDATA[select new Family(mother, mate, offspr)
from DomesticCat as mother
join mother.mate as mate
left join mother.kittens as offspr]]></programlisting>
<para>
あるいは <literal>Family</literal> クラスが適切なコンストラクタを持っているとするならば、
</para>
<para>
select節に <literal>as</literal> を使って別名をつけることもできます。
</para>
<programlisting><![CDATA[select max(bodyWeight) as max, min(bodyWeight) as min, count(*) as n
from Cat cat]]></programlisting>
<para>
<literal>select new map</literal> と一緒に使うときに最も役立ちます:
</para>
<programlisting><![CDATA[select new map( max(bodyWeight) as max, min(bodyWeight) as min, count(*) as n )
from Cat cat]]></programlisting>
<para>
このクエリは別名からselectした値へ <literal>Map</literal> を返します。
</para>
</sect1>
<sect1 id="queryhql-aggregation">
<title>集約関数</title>
<para>
HQLのクエリはプロパティの集約関数の結果も返せます
</para>
<programlisting><![CDATA[select avg(cat.weight), sum(cat.weight), max(cat.weight), count(cat)
from Cat cat]]></programlisting>
<!-- NO LONGER SUPPORTED
<para>
Collections may also appear inside aggregate functions in the <literal>select</literal>
clause.
</para>
<programlisting><![CDATA[select cat, count( elements(cat.kittens) )
from Cat cat group by cat]]></programlisting>
-->
<para>
サポートしている集約関数は以下のものです。
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
<literal>avg(...), sum(...), min(...), max(...)</literal>
</para>
</listitem>
<listitem>
<para>
<literal>count(*)</literal>
</para>
</listitem>
<listitem>
<para>
<literal>count(...), count(distinct ...), count(all...)</literal>
</para>
</listitem>
</itemizedlist>
<para>
select節において算術操作、連結と承認されたSQL関数を使うことができます。
</para>
<programlisting><![CDATA[select cat.weight + sum(kitten.weight)
from Cat cat
join cat.kittens kitten
group by cat.id, cat.weight]]></programlisting>
<programlisting><![CDATA[select firstName||' '||initial||' '||upper(lastName) from Person]]></programlisting>
<para>
SQLと同じ意味を持つ <literal>distinct</literal><literal>all</literal> キーワードを使うことができます。
</para>
<programlisting><![CDATA[select distinct cat.name from Cat cat
select count(distinct cat.name), count(cat) from Cat cat]]></programlisting>
</sect1>
<sect1 id="queryhql-polymorphism">
<title>ポリモーフィックなクエリ</title>
<para>
次のようなクエリ:
</para>
<programlisting><![CDATA[from Cat as cat]]></programlisting>
<para>
<literal>Cat</literal> インスタンスだけではなく、<literal>DomesticCat</literal>
のようなサブクラスも返されます。Hibernateクエリは <emphasis>どんな</emphasis>
Javaクラスやインターフェイスも <literal>from</literal> 節に入れることができます。
クエリはそのクラスを拡張した、もしくはインターフェイスを実装した全ての永続クラスを返します。
次のクエリは永続オブジェクトをすべて返します:
</para>
<programlisting><![CDATA[from java.lang.Object o]]></programlisting>
<para>
<literal>Named</literal> インターフェイスは様々な永続クラスによって実装されます。:
</para>
<programlisting><![CDATA[from Named n, Named m where n.name = m.name]]></programlisting>
<para>
最後の二つのクエリは、二つ以上のSQL <literal>SELECT</literal> を要求していることに注意してください。
このことは <literal>order by</literal> 節がリザルトセット全体を正確には整列しないことを意味します
(さらにそれは、<literal>Query.scroll()</literal> を使用してこれらのクエリを呼ぶことができないことを意味します。)。
</para>
</sect1>
<sect1 id="queryhql-where">
<title>where節</title>
<para>
<literal>where</literal> 節は返されるインスタンスのリストを絞ることができます。
もし別名がない場合、名前でプロパティを参照します。
</para>
<programlisting><![CDATA[from Cat where name='Fritz']]></programlisting>
<para>
もし別名がある場合、修飾名を使ってください。
</para>
<programlisting><![CDATA[from Cat as cat where cat.name='Fritz']]></programlisting>
<para>
名前が'Fritz'という <literal>Cat</literal> のインスタンスを返します。
</para>
<programlisting><![CDATA[select foo
from Foo foo, Bar bar
where foo.startDate = bar.date]]></programlisting>
<para>
上のHQLは、<literal>Foo</literal><literal>startDate</literal> プロパティと等しい
<literal>date</literal> プロパティを持った <literal>bar</literal> インスタンスが存在する、
すべての <literal>Foo</literal> インスタンスを返します。
コンパウンドパス式例えば「cat.mate.name」<literal>where</literal> 節を非常に強力にします。注目:
</para>
<programlisting><![CDATA[from Cat cat where cat.mate.name is not null]]></programlisting>
<para>
このクエリはテーブル結合内部結合を持つSQLクエリに変換されます。
その代わりに以下のように書くと、
</para>
<programlisting><![CDATA[from Foo foo
where foo.bar.baz.customer.address.city is not null]]></programlisting>
<para>
もし上のクエリを記述したらクエリ内に4つのテーブル結合を必要とするSQLクエリに変換されます。
</para>
<para>
<literal>=</literal> 演算子は以下のように、プロパティだけでなくインスタンスを比較するためにも使われます。:
</para>
<programlisting><![CDATA[from Cat cat, Cat rival where cat.mate = rival.mate]]></programlisting>
<programlisting><![CDATA[select cat, mate
from Cat cat, Cat mate
where cat.mate = mate]]></programlisting>
<para>
<literal>id</literal> (小文字)は特別なプロパティであり、
オブジェクトのユニークな識別子を参照するために使用できます。(さらに、そのプロパティ名を使用できます。)
</para>
<programlisting><![CDATA[from Cat as cat where cat.id = 123
from Cat as cat where cat.mate.id = 69]]></programlisting>
<para>
2番目のクエリは効率的です。テーブル結合が必要ありません
</para>
<para>
また複合識別子のプロパティも使用できます。ここで <literal>Person</literal>
<literal>country</literal><literal>medicareNumber</literal> からなる複合識別子を持つと仮定します。
</para>
<programlisting><![CDATA[from bank.Person person
where person.id.country = 'AU'
and person.id.medicareNumber = 123456]]></programlisting>
<programlisting><![CDATA[from bank.Account account
where account.owner.id.country = 'AU'
and account.owner.id.medicareNumber = 123456]]></programlisting>
<para>
もう一度言いますが、2番目のクエリにはテーブル結合が必要ありません。
</para>
<para>
同様に <literal>class</literal> は特別なプロパティであり、
ポリモーフィックな永続化におけるインスタンスのdiscriminator値にアクセスします。
where節に埋め込まれたJavaのクラス名はそのdiscriminator値に変換されます。
</para>
<programlisting><![CDATA[from Cat cat where cat.class = DomesticCat]]></programlisting>
<para>
またコンポーネントや複合ユーザ型(またそのコンポーネントのコンポーネントなど)のプロパティも指定できます。
しかし決して(コンポーネントのプロパティではなく)コンポーネント型のプロパティで終了するパス式を使わないでください。
例えばもし <literal>store.owner</literal><literal>address</literal>
コンポーネントを持つエンティティならば以下のような結果となります。
</para>
<programlisting><![CDATA[store.owner.address.city // okay
store.owner.address // error!]]></programlisting>
<para>
"any"型は特別なプロパティである <literal>id</literal><literal>class</literal> を持ち、
以下の方法で結合を表現することを可能にします(<literal>AuditLog.item</literal>
<literal>&lt;any&gt;</literal> でマッピングされたプロパティです)。
</para>
<programlisting><![CDATA[from AuditLog log, Payment payment
where log.item.class = 'Payment' and log.item.id = payment.id]]></programlisting>
<para>
<literal>log.item.class</literal><literal>payment.class</literal>
上記のクエリ中で全く異なるデータベースカラムの値を参照するということに注意してください。
</para>
</sect1>
<sect1 id="queryhql-expressions">
<title>Expressions 式</title>
<para>
SQLの <literal>where</literal> 節で記述することが出来る式のほとんどをHQLでも記述できます。:
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
算術演算子:<literal>+, -, *, /</literal>
</para>
</listitem>
<listitem>
<para>
2項比較演算子<literal>=, &gt;=, &lt;=, &lt;&gt;, !=, like</literal>
</para>
</listitem>
<listitem>
<para>
論理演算子:<literal>and, or, not</literal>
</para>
</listitem>
<listitem>
<para>
グループ分けを表す括弧:<literal>( )</literal>
</para>
</listitem>
<listitem>
<para>
<literal>in</literal>,
<literal>not in</literal>,
<literal>between</literal>,
<literal>is null</literal>,
<literal>is not null</literal>,
<literal>is empty</literal>,
<literal>is not empty</literal>,
<literal>member of</literal> and
<literal>not member of</literal>
</para>
</listitem>
<listitem>
<para>
"シンプル"なcase <literal>case ... when ... then ... else ... end</literal>
"探索的"なcase <literal>case when ... then ... else ... end</literal>
</para>
</listitem>
<listitem>
<para>
ストリングの連結 <literal>...||...</literal> または <literal>concat(...,...)</literal>
</para>
</listitem>
<listitem>
<para>
<literal>current_date()</literal>, <literal>current_time()</literal>,
<literal>current_timestamp()</literal>
</para>
</listitem>
<listitem>
<para>
<literal>second(...)</literal>, <literal>minute(...)</literal>,
<literal>hour(...)</literal>, <literal>day(...)</literal>,
<literal>month(...)</literal>, <literal>year(...)</literal>,
</para>
</listitem>
<listitem>
<para>
EJB-QL 3.0で定義されている関数や演算子: <literal>substring(), trim(),
lower(), upper(), length(), locate(), abs(), sqrt(), bit_length()</literal>
</para>
</listitem>
<listitem>
<para>
<literal>coalesce()</literal><literal>nullif()</literal>
</para>
</listitem>
<listitem>
<para>
数字や時間の値をStringにコンバートする <literal>str()</literal>
</para>
</listitem>
<listitem>
<para>
2番目の引数がHibernate型の名前である <literal>cast(... as ...)</literal>
<literal>extract(... from ...)</literal>
ただし使用するデータベースがANSI <literal>cast()</literal><literal>extract()</literal>
をサポートする場合に限ります。
</para>
</listitem>
<listitem>
<para>
結合したインデックス付きのコレクションの別名に適用されるHQLの
<literal>index()</literal> 関数。
</para>
</listitem>
<listitem>
<para>
コレクション値のパス式を取るHQL関数 <literal>size(), minelement(), maxelement(),
minindex(), maxindex()</literal>
<literal>some, all, exists, any, in</literal> を使って修飾することができる特別な
<literal>elements()</literal><literal>indices</literal> 関数と一緒に使います。
</para>
</listitem>
<listitem>
<para>
<literal>sign()</literal>, <literal>trunc()</literal>, <literal>rtrim()</literal>, <literal>sin()</literal>
のようなデータベースがサポートするSQLスカラ関数。
</para>
</listitem>
<listitem>
<para>
JDBCスタイルの位置パラメータ <literal>?</literal>
</para>
</listitem>
<listitem>
<para>
名前付きパラメータ: <literal>:name</literal>, <literal>:start_date</literal>, <literal>:x1</literal>
</para>
</listitem>
<listitem>
<para>
SQLリテラル<literal>'foo'</literal>, <literal>69</literal>, <literal>6.66E+2</literal>,
<literal>'1970-01-01 10:00:01.0'</literal>
</para>
</listitem>
<listitem>
<para>
Javaの <literal>public static final</literal> 定数:<literal>eg.Color.TABBY</literal>
</para>
</listitem>
</itemizedlist>
<para>
<literal>in</literal><literal>between</literal> は以下のように使用できます。:
</para>
<programlisting><![CDATA[from DomesticCat cat where cat.name between 'A' and 'B']]></programlisting>
<programlisting><![CDATA[from DomesticCat cat where cat.name in ( 'Foo', 'Bar', 'Baz' )]]></programlisting>
<para>
また、否定形で記述することもできます。
</para>
<programlisting><![CDATA[from DomesticCat cat where cat.name not between 'A' and 'B']]></programlisting>
<programlisting><![CDATA[from DomesticCat cat where cat.name not in ( 'Foo', 'Bar', 'Baz' )]]></programlisting>
<para>
同様に <literal>is null</literal><literal>is not null</literal> はnull値をテストするために使用できます。
</para>
<para>
Hibernate設定ファイルでHQL query substitutionsを定義すれば、boolean値を式の中で簡単に使用できま。
</para>
<programlisting><![CDATA[<property name="hibernate.query.substitutions">true 1, false 0</property>]]></programlisting>
<para>
こうすることで下記のHQLをSQLに変換するときに <literal>true</literal> ,
<literal>false</literal> キーワードは <literal>1</literal> , <literal>0</literal> に置き換えられます。:
</para>
<programlisting><![CDATA[from Cat cat where cat.alive = true]]></programlisting>
<para>
特別なプロパティ <literal>size</literal>、または特別な関数 <literal>size()</literal>
を使ってコレクションのサイズをテストできます。:
</para>
<programlisting><![CDATA[from Cat cat where cat.kittens.size > 0]]></programlisting>
<programlisting><![CDATA[from Cat cat where size(cat.kittens) > 0]]></programlisting>
<para>
インデックス付きのコレクションでは、<literal>minindex</literal><literal>maxindex</literal>
関数を使って、インデックスの最小値と最大値を参照できます。
同様に、<literal>minelement</literal><literal>maxelement</literal> を使って、
基本型のコレクション要素の最小値と最大値を参照できます。
</para>
<programlisting><![CDATA[from Calendar cal where maxelement(cal.holidays) > current_date]]></programlisting>
<programlisting><![CDATA[from Order order where maxindex(order.items) > 100]]></programlisting>
<programlisting><![CDATA[from Order order where minelement(order.items) > 10000]]></programlisting>
<para>
コレクションの要素やインデックスのセット(<literal>elements</literal><literal>indices</literal>
関数)、または副問い合わせ(後述)の結果が受け取れるときは、
SQL関数 <literal>any, some, all, exists, in</literal> がサポートされます。
</para>
<programlisting><![CDATA[select mother from Cat as mother, Cat as kit
where kit in elements(foo.kittens)]]></programlisting>
<programlisting><![CDATA[select p from NameList list, Person p
where p.name = some elements(list.names)]]></programlisting>
<programlisting><![CDATA[from Cat cat where exists elements(cat.kittens)]]></programlisting>
<programlisting><![CDATA[from Player p where 3 > all elements(p.scores)]]></programlisting>
<programlisting><![CDATA[from Show show where 'fizard' in indices(show.acts)]]></programlisting>
<para>
<literal>size</literal>, <literal>elements</literal>, <literal>indices</literal>,
<literal>minindex</literal>, <literal>maxindex</literal>, <literal>minelement</literal>,
<literal>maxelement</literal> はHibernate3のwhere節だけで利用可能であることに注意してください。
</para>
<para>
インデックス付きのコレクションarrays, lists, mapsの要素は、
インデックスで参照できますwhere節内でのみ
</para>
<programlisting><![CDATA[from Order order where order.items[0].id = 1234]]></programlisting>
<programlisting><![CDATA[select person from Person person, Calendar calendar
where calendar.holidays['national day'] = person.birthDay
and person.nationality.calendar = calendar]]></programlisting>
<programlisting><![CDATA[select item from Item item, Order order
where order.items[ order.deliveredItemIndices[0] ] = item and order.id = 11]]></programlisting>
<programlisting><![CDATA[select item from Item item, Order order
where order.items[ maxindex(order.items) ] = item and order.id = 11]]></programlisting>
<para>
<literal>[]</literal> 内部の式は、算術式でも構いません。
</para>
<programlisting><![CDATA[select item from Item item, Order order
where order.items[ size(order.items) - 1 ] = item]]></programlisting>
<para>
一対多関連や値のコレクションの要素に対しては、HQLは組み込みの <literal>index()</literal> 関数も用意しています。
</para>
<programlisting><![CDATA[select item, index(item) from Order order
join order.items item
where index(item) < 5]]></programlisting>
<para>
ベースとなるデータベースがサポートしているスカラーSQL関数が使用できます
</para>
<programlisting><![CDATA[from DomesticCat cat where upper(cat.name) like 'FRI%']]></programlisting>
<para>
もしまだ全てを理解していないなら、下のクエリをSQLでどれだけ長く、読みづらく出来るか考えてください。:
</para>
<programlisting><![CDATA[select cust
from Product prod,
Store store
inner join store.customers cust
where prod.name = 'widget'
and store.location.name in ( 'Melbourne', 'Sydney' )
and prod = all elements(cust.currentOrder.lineItems)]]></programlisting>
<para>
<emphasis>ヒント:</emphasis> 例えばこのように出来ます。
</para>
<programlisting><![CDATA[SELECT cust.name, cust.address, cust.phone, cust.id, cust.current_order
FROM customers cust,
stores store,
locations loc,
store_customers sc,
product prod
WHERE prod.name = 'widget'
AND store.loc_id = loc.id
AND loc.name IN ( 'Melbourne', 'Sydney' )
AND sc.store_id = store.id
AND sc.cust_id = cust.id
AND prod.id = ALL(
SELECT item.prod_id
FROM line_items item, orders o
WHERE item.order_id = o.id
AND cust.current_order = o.id
)]]></programlisting>
</sect1>
<sect1 id="queryhql-ordering">
<title>order by節</title>
<para>
クエリが返すlistは、返されるクラスやコンポーネントの任意の属性によって並べ替えられます。
</para>
<programlisting><![CDATA[from DomesticCat cat
order by cat.name asc, cat.weight desc, cat.birthdate]]></programlisting>
<para>
オプションの <literal>asc</literal><literal>desc</literal> はそれぞれ昇順か降順の整列を示します。
</para>
</sect1>
<sect1 id="queryhql-grouping">
<title>group by節</title>
<para>
集約値を返すクエリは、返されるクラスやコンポーネントの任意のプロパティによってグループ化できます。:
</para>
<programlisting><![CDATA[select cat.color, sum(cat.weight), count(cat)
from Cat cat
group by cat.color]]></programlisting>
<programlisting><![CDATA[select foo.id, avg(name), max(name)
from Foo foo join foo.names name
group by foo.id]]></programlisting>
<para>
<literal>having</literal> 節も使えます。
</para>
<programlisting><![CDATA[select cat.color, sum(cat.weight), count(cat)
from Cat cat
group by cat.color
having cat.color in (eg.Color.TABBY, eg.Color.BLACK)]]></programlisting>
<para>
もし使用するデータベースがサポートしているなら、
<literal>having</literal><literal>order by</literal> 節でSQL関数と集約関数が使えます
例えばMySQLにはありません
</para>
<programlisting><![CDATA[select cat
from Cat cat
join cat.kittens kitten
group by cat
having avg(kitten.weight) > 100
order by count(kitten) asc, sum(kitten.weight) desc]]></programlisting>
<para>
<literal>group by</literal> 節や <literal>order by</literal> 節に
算術式を含むことができないことに注意してください。
</para>
</sect1>
<sect1 id="queryhql-subqueries" revision="2">
<title>副問い合わせ</title>
<para>
サブセレクトをサポートするデータベースのため、Hibernateは副問い合わせをサポートしています。
副問い合わせは括弧で囲まなければなりませんSQLの集約関数呼び出しによる事が多いです
関連副問い合わせ(外部クエリ中の別名を参照する副問い合わせのこと)さえ許可されます。
</para>
<programlisting><![CDATA[from Cat as fatcat
where fatcat.weight > (
select avg(cat.weight) from DomesticCat cat
)]]></programlisting>
<programlisting><![CDATA[from DomesticCat as cat
where cat.name = some (
select name.nickName from Name as name
)]]></programlisting>
<programlisting><![CDATA[from Cat as cat
where not exists (
from Cat as mate where mate.mate = cat
)]]></programlisting>
<programlisting><![CDATA[from DomesticCat as cat
where cat.name not in (
select name.nickName from Name as name
)]]></programlisting>
<programlisting><![CDATA[select cat.id, (select max(kit.weight) from cat.kitten kit)
from Cat as cat]]></programlisting>
<para>
HQL副問い合わせは、selectまたはwhere節だけで使われることに注意してください。
</para>
<para>
selectリストに複数の式を持つ副問い合わせには、タプルを使うことができます。
</para>
<programlisting><![CDATA[from Cat as cat
where not ( cat.name, cat.color ) in (
select cat.name, cat.color from DomesticCat cat
)]]></programlisting>
<para>
いくつかのデータベースOracleやHSQLにはありませんでは、
他のコンテキストでもタプルが使えます。
例えば、クエリコンポーネントや複合ユーザ型においてです。
</para>
<programlisting><![CDATA[from Person where name = ('Gavin', 'A', 'King')]]></programlisting>
<para>
同等ですが、より冗長なクエリです:
</para>
<programlisting><![CDATA[from Person where name.first = 'Gavin' and name.initial = 'A' and name.last = 'King')]]></programlisting>
<para>
このようなことをしたくないのには2つの理由があります
1つ目は、データベースプラットフォーム間で完全な互換性はないからです。
2つ目は、クエリがマッピングドキュメントのプロパティの順序に依存するからです。
</para>
</sect1>
<sect1 id="queryhql-examples">
<title>HQLの例</title>
<para>
Hibernateクエリは非常に強力で複雑にできます。実際、クエリ言語の威力はHibernateの主要なセールスポイントの一つです。
ここに最近のプロジェクトで使用したクエリと非常によく似た例があります。
ほとんどのクエリはこれらの例より簡単に記述できることに注意してください!
</para>
<para>
以下のクエリは特定の顧客と与えられた最小の合計値に対する未払い注文の注文ID、
商品の数、注文の合計を合計値で整列して返します。
価格を決定する際、現在のカタログを使います。結果として返されるSQLクエリは
<literal>ORDER</literal>, <literal>ORDER_LINE</literal>, <literal>PRODUCT</literal>,
<literal>CATALOG</literal> および <literal>PRICE</literal> テーブルに対し4つの内部結合と
(関連しない)副問い合わせを持ちます。
</para>
<programlisting><![CDATA[select order.id, sum(price.amount), count(item)
from Order as order
join order.lineItems as item
join item.product as product,
Catalog as catalog
join catalog.prices as price
where order.paid = false
and order.customer = :customer
and price.product = product
and catalog.effectiveDate < sysdate
and catalog.effectiveDate >= 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]]></programlisting>
<para>
何て巨大なクエリなのでしょう! 普段私は副問い合わせをあまり使いません。したがって私のクエリは実際には以下のようになります。:
</para>
<programlisting><![CDATA[select order.id, sum(price.amount), count(item)
from Order as order
join order.lineItems as item
join item.product as product,
Catalog as catalog
join catalog.prices as price
where order.paid = false
and order.customer = :customer
and price.product = product
and catalog = :currentCatalog
group by order
having sum(price.amount) > :minAmount
order by sum(price.amount) desc]]></programlisting>
<para>
次のクエリは各ステータスの支払い数を数えます。ただしすべての支払いが現在の利用者による
最新のステータス変更である <literal>AWAITING_APPROVAL</literal> である場合を除きます。
このクエリは2つの内部結合と <literal>PAYMENT</literal>, <literal>PAYMENT_STATUS</literal> および
<literal>PAYMENT_STATUS_CHANGE</literal> テーブルに対する関連副問い合わせを備えたSQLクエリに変換されます。
</para>
<programlisting><![CDATA[select count(payment), status.name
from Payment as payment
join payment.currentStatus as status
join payment.statusChanges as statusChange
where payment.status.name <> 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]]></programlisting>
<para>
もし私がsetの代わりにlistとして <literal>statusChanges</literal> コレクションを
マッピングしたならば、はるかに簡単にクエリを記述できるでしょう。
</para>
<programlisting><![CDATA[select count(payment), status.name
from Payment as payment
join payment.currentStatus as status
where payment.status.name <> PaymentStatus.AWAITING_APPROVAL
or payment.statusChanges[ maxIndex(payment.statusChanges) ].user <> :currentUser
group by status.name, status.sortOrder
order by status.sortOrder]]></programlisting>
<para>
次のクエリは現在のユーザが所属する組織に対するアカウントおよび未払いの支払いを
すべて返すMS SQL Server
<literal>isNull()</literal> 関数を使用しています。
このクエリは3つの内部結合と1つの外部結合 、
そして <literal>ACCOUNT</literal>, <literal>PAYMENT</literal>, <literal>PAYMENT_STATUS</literal>,
<literal>ACCOUNT_TYPE</literal>, <literal>ORGANIZATION</literal> および
<literal>ORG_USER</literal> テーブルに対する副問い合わせ持ったSQLに変換されます。
</para>
<programlisting><![CDATA[select account, payment
from Account as account
left outer join account.payments as payment
where :currentUser in elements(account.holder.users)
and PaymentStatus.UNPAID = isNull(payment.currentStatus.name, PaymentStatus.UNPAID)
order by account.type.sortOrder, account.accountNumber, payment.dueDate]]></programlisting>
<para>
いくつかのデータベースについては、(関連させられた)副問い合わせの使用を避ける必要があるでしょう。
</para>
<programlisting><![CDATA[select account, payment
from Account as account
join account.holder.users as user
left outer join account.payments as payment
where :currentUser = user
and PaymentStatus.UNPAID = isNull(payment.currentStatus.name, PaymentStatus.UNPAID)
order by account.type.sortOrder, account.accountNumber, payment.dueDate]]></programlisting>
</sect1>
<sect1 id="queryhql-bulk" revision="2">
<title>大量のUPDATEとDELETE</title>
<para>
HQLは今は <literal>update</literal><literal>delete</literal><literal>insert ... select ...</literal>
ステートメントをHQLに入れることをサポートしています。
<xref linkend="batch-direct"/> に詳細があります。
</para>
</sect1>
<sect1 id="queryhql-tipstricks">
<title>Tips &amp; Tricks</title>
<para>
実際に結果を返さなくてもクエリの結果数を数えることができます。:
</para>
<programlisting><![CDATA[( (Integer) session.iterate("select count(*) from ....").next() ).intValue()]]></programlisting>
<para>
コレクションのサイズにより結果を並べ替えるためには以下のクエリを使用します。:
</para>
<programlisting><![CDATA[select usr.id, usr.name
from User as usr
left join usr.messages as msg
group by usr.id, usr.name
order by count(msg)]]></programlisting>
<para>
使用しているデータベースがサブセレクトをサポートする場合、クエリのwhere節でサイズによる選択条件を設定できます:
</para>
<programlisting><![CDATA[from User usr where size(usr.messages) >= 1]]></programlisting>
<para>
使用しているデータベースがサブセレクトをサポートしない場合は、次のクエリを使用してください:
</para>
<programlisting><![CDATA[select usr.id, usr.name
from User usr.name
join usr.messages msg
group by usr.id, usr.name
having count(msg) >= 1]]></programlisting>
<para>
内部結合をしているせいで上の解決法がmessageの件数が
ゼロの <literal>User</literal> を返すことができないならば、以下の形式が使えます。
</para>
<programlisting><![CDATA[select usr.id, usr.name
from User as usr
left join usr.messages as msg
group by usr.id, usr.name
having count(msg) = 0]]></programlisting>
<para>
JavaBeanのプロパティは、名前付きのクエリパラメータに結びつけることが出来ます。
</para>
<programlisting><![CDATA[Query q = s.createQuery("from foo Foo as foo where foo.name=:name and foo.size=:size");
q.setProperties(fooBean); // fooBean has getName() and getSize()
List foos = q.list();]]></programlisting>
<para>
コレクションはフィルタ付き <literal>Query</literal> インターフェイスを使用することでページをつけることができます。:
</para>
<programlisting><![CDATA[Query q = s.createFilter( collection, "" ); // the trivial filter
q.setMaxResults(PAGE_SIZE);
q.setFirstResult(PAGE_SIZE * pageNumber);
List page = q.list();]]></programlisting>
<para>
コレクションの要素はクエリフィルタを使って、並べ替えやグループ分けが出来ます。:
</para>
<programlisting><![CDATA[Collection orderedCollection = s.filter( collection, "order by this.amount" );
Collection counts = s.filter( collection, "select this.type, count(this) group by this.type" );]]></programlisting>
<para>
コレクションを初期化せずにコレクションのサイズを得ることができます。:
</para>
<programlisting><![CDATA[( (Integer) session.iterate("select count(*) from ....").next() ).intValue();]]></programlisting>
</sect1>
</chapter>