EJB 3.0(Public Draft)入門記 Java Persistence API Chapter4 その4

EJB QLのFROM節についてです。
結構聞きなれない言葉というか概念がいくつか出てきます。今までもそうですけど説明が後ろの方にあるくせに説明の前でいきなしその言葉使うんですよね。ちょっと腹立つ。
あと新しい言葉って訳せないんですよね...

4.4 The FROM Clause and Navigational Declarations

identification variableとかpath expressionとかいう概念が重要そうです。
identification variableとはエンティティの抽象スキーマ型のインスタンスを指すらしいです。identification variableをとりあえず識別変数と訳しますが、これは抽象スキーマ型の後ろにつける値のことのようです。オプションですがasを使えばasの後ろについたりもします。たとえば

select o from Order o join o.lineItems l

というEJB QLの場合、「o」とか「l」のことです。「o」や「l」は「Order」や「LineItem」のエイリアスだと思っていたんですがエイリアスというよりも「Order」型や「LineItem」型のインスタンスなんですね。「select o」とすることに不自然さを感じていたのですが腑に落ちました。インスタンスを取得することを示していると考えればいいんじゃないでしょうか。

以下、基本的に前回のエンティティ定義をもとに進めていきます。

4.4.1 Identifires

識別変数で使用可能な文字と予約語の話題です。Idアノテーションとかの話とは関係なさそうです。識別変数で使用可能な文字のルールはJavaと同じようです。予約語になっているものはだいたい予想できるもの(JOINとかWHEREとか、前バージョンのEJB QLの予約語とか)です。将来のEJB QLのリリースで含まれるかもしれないのでSQL予約語は使わないほうがいいよとのことです。

4.4.2 Identification Variables

識別変数はFROM節以外で定義してはいけないそうです。
識別変数名は当然予約語と同じであってはだめなのですが、それ以外に同一の永続ユニット内の次のものと同じでは駄目だそうです。

  • エンティティの名称(EntityアノテーションXML記述子に指定できる名称)
  • abstaract-schema-name(EJB 2.1の場合)
  • ejb-name(EJB 2.1の場合)

4.4.3 Range Variables Declarations

range variablesの訳は何だろう。範囲変数?これでいいか。範囲変数はnavigationのrootとなる識別変数のことのようです。範囲変数からはステートフィールドや関連フィールドをたどれます。範囲変数はjoinの結合元になったりもするようです。

select o from Order o join o.lineItems l

上記EJB QLの場合「o」が範囲変数です。

4.4.4 Path Expressions

識別変数の後ろにナビゲーション演算子(.)とステートフィールドもしくは関連フィールドがつづいたものをpath expressionというらしいです。訳すならパス式?パス式を使ってナビゲーションができるということですね。このナビゲーションは内部結合を使って解決されるらしいです。値がnullなどの場合は結果セットの決定に影響しないらしいです。
コレクションをあらわすパス式の後ろにパスを伸ばすことはできないらしいです。
どういうことかというと次のEJB QLは不適切です。「o.lineItems.product」とLineItemのコレクションに対してproductを指定しているからです。

select o from Order o where o.lineItems.product.name = 'お茶' 

次のEJB QLはOKです。「o.lineItems l」といったん識別変数を定義し、それから「l.product.name」とパス式をつかっているからです。

select o from Order o join o.lineItems l where l.product.name = 'お茶'

4.4.5 Joins

次のEJB QLは直積を返します。

select o from Order o, LineItem l

次のEJB QLは暗黙的な内部結合の結果を返します。

select o from Order o, LineItem l where o = l.order 

このような内部結合の使い方は明示的に結合を指定するよりも一般的じゃないそうです。明示的に結合したほうがわかりやすいですもんね。
次に示すような内部結合と外部結合をサポートしているらしいです。

4.4.5.1 Inner Joins(Relationship Joins)
内部結合はinnerをつけてもつけなくてもいいそうです。SQLと同じですね。

select o from Order o join o.lineItems l
select o from Order o inner join o.lineItems l

IN演算子を使ってもOKらしいです。

select object(o) from Order o, in(o.lineItems) l

上のEJB QLはどれもまったく同じSQLに変換されました。

Hibernate: select order0_.id as id2_, order0_.shippingAddress_id as shipping2_2_, order0_.billingAddress_id as billingA3_2_ 
  from ODR order0_ inner join LineItem lineitems1_ on order0_.id=lineitems1_.order_id

4.4.5.2 Left Outer Joins
左外部結合もouterがあってもなくてもOKです。

select o from Order o left join o.lineItems l
select o from Order o left outer join o.lineItems l

うえの2つのEJB QLは次のようなSQLになりました。

Hibernate: select order0_.id as id2_, order0_.shippingAddress_id as shipping2_2_, order0_.billingAddress_id as billingA3_2_ from ODR order0_ left outer join LineItem lineitems1_ on order0_.id=lineitems1_.order_id

右外部結合はできないみたいです。試してみたら怒られました。

4.4.5.3 Fetch Joins
個別のクエリを別々に実行するのではなく一度のクエリで関連先のデータもいっしょに取得してしまおうというものがFETCH JOINです。FETCH JOIN節の右側には関連の参照先を指定するのですが、そいつには識別変数を指定してはいけないらしいです。(JBoss(というかHibernate)では特に怒られないんですけど)

fetch join試してみました。EJB QLのSELECT節にはひとつの変数しか指定していないのにもかかわらず、SQLのSELECT節には関連先のカラムが含まれます。ところで、言うときはfetch joinと言ってfetchが前にくるんですが、EJB QLに書くときはjoinの後ろにfetchを指定してjoin fetchになるんですね、まあいいけど。
内部結合でfetchした場合

select o from Order o inner join fetch o.lineItems

そのときのSQL。LineItemのデータもfetchしてます。

Hibernate: select order0_.id as id2_0_, lineitems1_.id as id1_1_, order0_.shippingAddress_id as shipping2_2_0_, order0_.billingAddress_id as billingA3_2_0_, 
  lineitems1_.quantity as quantity1_1_, lineitems1_.shipped as shipped1_1_, lineitems1_.order_id as order4_1_1_, lineitems1_.product_id as product5_1_1_, 
  lineitems1_.order_id as order4_0__, lineitems1_.id as id0__ 
  from ODR order0_ inner join LineItem lineitems1_ on order0_.id=lineitems1_.order_id


外部結合でfetchした場合

select o from Order o left outer join fetch o.lineItems

そのときのSQL。LineItemのデータもfetchしてます。

Hibernate: select order0_.id as id2_0_, lineitems1_.id as id1_1_, order0_.shippingAddress_id as shipping2_2_0_, order0_.billingAddress_id as billingA3_2_0_, 
  lineitems1_.quantity as quantity1_1_, lineitems1_.shipped as shipped1_1_, lineitems1_.order_id as order4_1_1_, lineitems1_.product_id as product5_1_1_, 
  lineitems1_.order_id as order4_0__, lineitems1_.id as id0__ 
  from ODR order0_ left outer join LineItem lineitems1_ on order0_.id=lineitems1_.order_id


複数のfetchも試してみました。LineItemだけでなくProductもfetchしてます。

select o from Order o left join fetch o.lineItems l left join fetch l.product

LineItemとProductのデータをfetchしてます。

Hibernate: select order0_.id as id2_0_, lineitems1_.id as id1_1_, product2_.id as id3_2_, order0_.shippingAddress_id as shipping2_2_0_, 
  order0_.billingAddress_id as billingA3_2_0_, lineitems1_.quantity as quantity1_1_, lineitems1_.shipped as shipped1_1_, 
  lineitems1_.order_id as order4_1_1_, lineitems1_.product_id as product5_1_1_, lineitems1_.order_id as order4_0__, lineitems1_.id as id0__, 
  product2_.name as name3_2_, product2_.price as price3_2_, product2_.productType as productT4_3_2_ 
  from ODR order0_ 
  left outer join LineItem lineitems1_ on order0_.id=lineitems1_.order_id 
  left outer join Product product2_ on lineitems1_.product_id=product2_.id

問題なくできました。でもこれEJB QL的にOKかはわかりません。FETCH JOIN節で識別変数を指定するなっていっているんですが、LineItemとProductをfetch joinをするときに識別変数使わざるを得ないのです。

4.4.6 Collection Member Declarations

パス式を使ったナビゲーションで得られるコレクションの値の定義ということですが、前バージョンのEJB QLとの互換性を考えなければ使う必要ないかな?

select distinct o from Order o join o.lineItems l join l.product p
where p.productType = '食'

IN演算子を使うと上記のEJB QLは次のようになります。

select distinct o from Order o, in(o.lineItems) l
where l.product.productType = '食'

4.4.7 EJB QL and SQL

EJB QLはSQLと同じようにFROM節を扱います。EJB QLに定義された識別変数は、たとえWHERE句で使われていなくてもクエリの結果に影響を与えるそうです。
たとえば次のようなEJB QLがあるとします。

select o from Order as o, IN(o.lineitems) l, Product p

OrderとLineItemにデータがあってProductにデータがない場合、このEJB QLは1件も結果を返しません。「Product p」を定義からはずせば結果は返ってきます。

ちなみに上のEJB QLは次のようなSQLに変換されるので結果が返ってこないのは当然といえそうです。

select order0_.id as id2_, order0_.shippingAddress_id as shipping2_2_, order0_.billingAddress_id as billingA3_2_ 
from ODR order0_ inner join LineItem lineitems1_ on order0_.id=lineitems1_.order_id, Product product2_

4.4.8 Polymorphism

EJB QLは自動でポリモーフィックです。これは3.5.4 Polymophic Queriesのとこでもでてましたね。入門記ではこのあたりです。http://d.hatena.ne.jp/taedium/20051016#p2


4.4 The FROM Clause and Navigational Declarations終わりです。