EJB3.0(Early Draft) 入門記 Chapter 7 その1

ちょっと間が開いてしまいましたが,今日から新しい章,
Chapter 7 Query Language
へ進みます.
Entity Bean のための問い合せ言語,いわゆる EJB QL ですね.かなり機能強化されているとのことです.
その項目一覧.

  • バルク更新と削除
  • 結合
  • GROUP BY と HAVING
  • プロジェクション
  • 副問い合せ

これらは,EJB 2.x の Entity Bean の finder/select メソッドからも使えるそうです.
EJB QL は,動的および静的な問い合せ双方でフルに使えて,それはパラメタライズできるとか.パラメタライズ... generics
なんて一瞬思ったのですが,そういうわけじゃなくて,問い合せにいわゆるバインド変数を使うことができるということみたい.心より恥じる.
そのバインド変数 (パラメタ) は名前を付けることができるそうです.
ということで,強化された機能の解説,始まり始まり〜.


7.1 Bulk Operations
バルク更新および削除.てっきり JDBC のバッチ更新的なものかと思いきや,全然違いました.心より恥じる.
サンプルが載っているので一番簡単なやつを引用.

DELETE
FROM Customer c
WHERE c.status = ‘inactive’
    AND c.orders IS EMPTY

ようするに,WHERE 句で条件にマッチしたエンティティをまとめて更新または削除できるよ,ということですか.エンティティというかもろに行ですな.
そうかぁ,今までは EntityBean のインスタンスを手に入れて更新/削除しなきゃいけなかったわけで,これだけでも大進歩ということですか.笑っちゃいますね.(^^;
なお,UPDATE または DELETE で指定することができる Entity Bean のクラスは一つだけとのこと.
それから,Entity Bean の状態を更新したトランザクションの中でバルク更新または削除すべきでないとのこと.ふむ.トランザクションがコミットされるまで,Entity Bean の状態がストレージ (RDBMS) に反映されるとは限らないということで,意図した結果にならないかもしれないということでしょうか.もしかして落とし穴候補?
ドキュメントには明示されていませんが,バルク削除が行われた場合,Entity Bean の ejbRemove() は呼び出されないものと予想されます.EJB3.0 ではコールバックメソッドがオプションなので,あまり問題ないと思いますけれど.


7.2 Joins
EJB 3.0 では,4種類の結合をサポートするようです.


7.2.1 Inner Join (General Case)
普通の内部結合.普通って何?
説明しよう.それは,関連をたどるのとは違うやつ全部だ.たぶん.きっと.じゃないかなぁ.
ともかく,WHERE 句で結合条件を指定すればいいらしいです.で,WEHERE 句を省略すると直積になるとか.ん? それを内部結合と呼ぶのか?
とりあえず例.

select c from customer c, employee e where c.hatsize = e.shoesize

たしかに普通だ.そのまんま普通だ.
が,なぜに FROM 句で結合条件を書くようにしないのだろう?

select c from customer c inner join employee e on c.hatsize = e.shoesize

ではだめなのかな?
結合の条件と選択の条件は明確に区別できた方がいいと思うけど.


7.2.2 Inner Join (Relationship Join)
こっちは普通じゃない内部結合.普通じゃないのは関連で結合するからだ.
ともかく例を見るべし.

SELECT c FROM Customer c JOIN c.orders WHERE c.status = 1

WHERE 句がついてますが,これは本題とは関係なさそう.大事なのは JOIN c.orders だけと思われ.
ここで c.orders は,1対多とか多対多とかの関連であると注釈されているのでしょう.そして,そこには外部キーのカラム名なんかが定義されていて,それを使って結合してくれるのでしょう.
そうか,普通の内部結合は WHERE 句で,関連による結合は FROM 句で,ということかな? そういう区別ならしょうがないか.許す.
ちなみに上の問い合せは,EJB 2.x の次の問い合せと同等だとか.

SELECT OBJECT(c) FROM Customer c, IN(c.orders) o WHERE c.status = 1

うん,この構文は馴染みにくいので,これは改善といえそう.
BNF を見ると,この 2.x の構文もまだまだ有効みたいですが.


7.2.3 Left Outer Join
そして外部結合.いわゆる外部結合.でも左だけ.右はない.フルもない.そして例もない... (;_;)
BNF によると,どうやら上の JOINLEFT または LEFT OUTER にすればいいだけらしい.

SELECT c FROM Customer c LEFT c.orders WHERE c.status = 1

みたいな.
外部結合の使い方の筆頭は,プリフェッチだとか.N+1 な問い合せを避けるためですね.それには,外部結合をフェッチ結合として使えとのこと.フェッチ結合って何?


7.2.4 Fetch Joins
説明しよう.フェッチ結合とは,問い合せの副作用として,関連をフェッチしてくれる結合である.たぶん.
まずは例.

SELECT DISTINCT c
FROM Customer c LEFT JOIN FETCH c.orders
WHERE c.address.state = ’CA’

この場合,問い合せ結果として戻されるのは Customer だけですが,問い合せの副作用として,選択された Customer に関連づけられた Ordre が初期化されます.らしいです.
Order が持つ関連をどうするか (フェッチするかしないかでしょう) は,Order クラスのメタデータにより指定できるようです.
なお,フェッチ結合の右側は,問い合せ結果として戻される Entity Bean (この場合は Customer) に関連づけれた Entity Bean でなければならないとのことです.
それから,フェッチ結合の右側の Entity Bean は,問い合せの他の場所 (SELECT 句やWHERE 句など) で使うことができないとのことです.
さらに,BNF によるとフェッチ結合は外部結合だけでなく,内部結合とも組み合わせることができるようです.


7.3 Projection
射影.リレーショナル用語ですね.情報処理試験くらいでしか耳にしない用語の気がしないでもない.(^^;
用語としては使わなくても,射影そのものは SQL では日常的に使いますね.SELECT 句にカラムを並べたてるアレのことなので.
射影は RDB/SQL ではごく普通に使うわけですが,O/Rマッピング的にはイマイチだということは「インピーダンス・ミスマッチ」や「Hibernate 入門記 コレクションその11 Lazy Initialization」でも書いたとおり.
にもかかわらず,EJB3.0 では射影をサポートするとのこと.
こんな感じ.

SELECT c.id, c.status
FROM Customer c JOIN c.orders o
WHERE o.count > 100

この結果は,java.lang.Object の配列として返されるらしいです.Hibernate の HQL と同じっぽい.
しかし... Object の配列なんかうれしくないやい!
大丈夫です,そんなあなたのためにステキな機能が用意されています.
その例.

SELECT new CustomerDetails(c.id, c.status, o.count)
FROM Customer c JOIN c.orders o
WHERE o.count > 100

このように問い合せをすると,CustomerDetails のリストが返されるのですが,この CustomerDetails は Entity Bean でなくても構わないのです.ほほーっ.いいかも.あっ,でも Hibernate でも同じことができるみたい♪


7.4 Group By, Having
まずは例.とにかく例.

SELECT c.status, avg(c.filledOrderCount), count(c)
FROM Customer c
GROUP BY c.status
HAVING s.status IN (1, 2)

ふむ.普通に使えるっぽい.
問い合せの結果は射影と同じで java.lang.Object の配列かな? きっとそうなんだろうなぁ.
集約関数の引数を除いて,SELECT 句に記述できるのは GROUP BY 句に記述されたプロパティのみとのこと.
油断してましたが,集約関数自体は EJB 2.x でもサポートされていたのですね.知らなかった... 心より恥じる.
でも,GROUP BY なしで集約関数とは中途半端な...


7.5 Subqueries
WHERE 句に副問い合せが書けるそうです.
ということで例.

SELECT goodCustomer
FROM Customer goodCustomer
WHERE goodCustomer.balance < (
    SELECT avg(c.balance) FROM Customer c)

もう一つの例.

SELECT DISTINCT emp
FROM Employee emp
WHERE EXISTS (
    SELECT spouseEmp
    FROM Employee spouseEmp
    WHERE spouseEmp = emp.spouse)

こっちの例は相関副問い合せですね.
ドキュメントに書いてあることはこれだけ.(^^;
例を載せて終わりとは... やってくれるなぁ.いいけど.


7.6 Support for Additional Functions
WHERE 句で使うことのできる関数が追加されているそうです.

  • UPPER
  • LOWER
  • TRIM
  • POSITION
  • CHARACTER_LENGTH
  • CHAR_LENGTH
  • BIT_LENGTH
  • CURRENT_TIME
  • CURRENT_DATE
  • CURRENT_TIMESTAMP

以上.


7.7 Polymorphism
EJB QL は,自動的にポリモーフィズムを扱ってくれるそうです.
FROM 句に書かれたクラスについては,その直接のインスタンスだけでなく,サブクラスのインスタンスも取ってきてくれるとのこと.
将来のドラフトでは,ポリモーフィズムを制限できるような問い合せの機能を追加する計画らしいです.


7.8 Named Parameters
名前付きのパラメータ.その例.

SELECT c
FROM Customer c
WHERE c.status = :stat

よかろう.


7.9 EJB 3.0 QL BNF
必要に応じて.


とうことで,駆け足でしたが 7 章終了です♪ やったね.
それにしても,結局のところ SQL なんですね.ひたすら SQL に近づいているだけじゃない?
SQL を書きたくない,書かせたくないという人達から CMP Entity Bean が期待されていたような気もするわけですが,そんな人達にとってこれは進歩なんでしょうか? ちょっと疑問.
自分は別に SQL 書いてもいいというか,仕事の方で使っている DAO なところは完全に SQL 主導なんで全然構いませんけれど...
なんにせよ,Hibernate 触っておけば怖くなさそう.ラッキー♪


ということで,次は 8 章,「Chapter 8 Enterprise Bean Context and Environment」へ進みます.