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

WHERE節やHAVING節で使われる条件式についてです。
EJB QLを使って動かしてみますが、エンティティの定義はChapter 4 その4で作成したものを使います。

4.6 Conditional Expressions

いきなし注意書きですが、直列化された形式もしくはlobsとしてマップされたステートフィールド(state-field)を条件式で扱うことは可搬性がないらしいです。Persistence APIの実装では、そのようなフィールドを含んだクエリの実行がデータベースではなくメモリ上で行われることを予期していないんだそうです。直列化された形式って画像とかでしょうか。画像とかlobsとかって扱ったことないのであれですが、なんとなくわかるような気はします。

4.6.1 Literals

文字リテラルはシングルクオテーションで囲みます('literal')。シングルクオテーションを文字として使いたいときはシングルクオテーションを2つ使います('literal''s')。SQLと同じですね。

小数点なしの正確な数値リテラル(7, -957, +62など)はJavaのlongの値の範囲で使えるそうです。

概数(科学表記や小数点つきの値)のリテラル(7E3, 7., -95.2など)はJavaのdoubleの値の範囲で使えるそうです。型を示すためにJavaの言語仕様にしたがってサフックスが使えるようです。サフィックスってFとかDとかのことだと思って「10.123F」とか「10.123D」をwhere句で使ってみたんですが、うまくいきませんでした。SQLにそのままわたってしまうのですよね。なぜ...まだ実装前?それとも何か勘違いしているのかも...

booleanのリテラルはTRUEとFALSEです。

4.6.2 Identification Variables

識別変数(identification variable)とは何かというと「select o from Order o」というEJB QLで言えば「o」のことです。ちなみに「識別変数」って勝手訳です。
SELECTやDELETEステートメントのWHERE節やHVAING節で使われる識別変数はFROM節で定義されなければいけないらしいです。UPDATEステートメントのときはUPDATE節で定義しなければいけないらしい。識別変数はコレクションのメンバやエンティティの抽象スキーマ型のインスタンスはあらわすけどコレクションそのものを示しはしないそうです。

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

上のクエリでは識別変数「l」は「o.lineItems」というコレクションを示す式の後に置かれていますが、「l」が意味しているのはコレクションのメンバである個々のlineItemで決してコレクション全体を意味しているわけじゃないよと言いたいのだと思います。

4.6.3 Path Expressions

コレクションをあらわすパス式をWHERE節やGROUP BY節で使っちゃ駄目みたいです。コレクションだと値が特定できないからかな?ただ次のようなクエリでは使ってもOKみたいです。

  • 空コレクションの比較式
  • コレクションのメンバ式
  • size演算子の引数として

それぞれの例はこんな感じ?一応全部動きました。2番目のやつは名前つきパラメータ使ってます。

select distinct o from Order o where o.lineItems is empty
select distinct o from Order o where :lineItem member of o.lineItems 
select distinct o from Order o where size(o.lineItems) > 2

3番目のやつはsize演算子というよりsize関数なんですけど...。size演算子ってもっと別のことを意味しているのかも?

4.6.4 Input Parameters

位置パラメータや名前つきパラメータが使えます。この2つはひとつクエリでは同時に使えないようです。Input ParameterはWHERE節やGROUP BY節でしか使えないらしいです。

4.6.4.1 Positional Parameters
位置パラメータには次のルールが適用されます。

  • 位置パラメータは?と整数で表す。たとえば、 ?1。
  • 位置パラメータの数値は1からはじまる。
  • クエリがfinderやselectメソッドに関連付けられている場合、input parameterの数はfinderやselectメソッドのinput parameterの数を超えてはいけないとかなんとか...

特に断りがないのですが最後のルールはEJB 2.1での話かな?

ところでgoogleで「位置指定パラメータ」という用語を見つけました。「位置指定パラメータ」のほうがわかりやすくていいと思うのですが、あんまり使われてないようです。どっちにしようかと迷ったのですがこのまま「位置パラメータ」と呼びます。

4.6.4.2 Named Parameters
名前つきパラメータについては以前にでてきましたね。パラメータであることを「:」で示します。こんな感じです。

select p from Product p where p.name = :name

名前つきパラメータはEJB 2.1のfinderやselectメソッドではサポートされていないそうです。

4.6.5 Conditional Expression Composition

...とりたてて書くことないですね。条件式には他の条件式や比較操作などが含まれるそうです。式の評価の順番は()で指定できるそうです。

4.6.6 Operators and Operator Precedence

演算子と優先度の順番です。優先度の低いものから次のようになるそうです。

  • ナビゲーション演算子 (「.」のこと)
  • 算術演算子
    • +, - 単項式
    • *, / 乗除
    • +, - 加減
  • 比較演算子
    • =, >, >=, <, <=, <>, [NOT] BETEWEEN, [NOT] LIKE, [NOT] IN, IS [NOT] NULL, IS [NOT] EMPTY, [NOT] MEMBER [OF]
  • 論理演算子
    • NOT
    • AND
    • OR

EJB QLに独特なのはナビゲーション演算子と比較演算子の中のIS [NOT] EMPTY と [NOT] MEMBER [OF] ぐらいかな。
ところで単項式が何かってわからなくて調べてしまいました。これって恥ずかしいことだったり?

4.6.7 Between Expressions

数と文字と日付について比較ができるみたい。SQLと同じ感じ。

select p from Product p where p.price between 50 and 120

当然次のように書いてもOK。

select p from Product p where p.price >= 50 and p.price <= 120

比較演算子におけるunknownとnullのルールが適用されるらしい。これもSQLと同じだよね? 詳細は4.12節にあるのでそのときに。

4.6.8 In Expressions

これもSQLと同じかな。値をカンマで区切って渡した場合。

select p from Product p where p.name in ('お茶', 'コーラ')

サブクエリもOKということなんですが、サブクエリがエンティティを返す場合はどうなるのでしょう。次のEJB QLを試してみます。

select l from LineItem l inner join l.product p where p in (select p2 from Product p2)

SQLは次のようになりました。

Hibernate: select lineitem0_.id as id1_, lineitem0_.quantity as quantity1_, lineitem0_.shipped as shipped1_, 
lineitem0_.order_id as order4_1_, lineitem0_.product_id as product5_1_ 
from LineItem lineitem0_ 
inner join Product product1_ on lineitem0_.product_id=product1_.id 
where product1_.id in (select product2_.id from Product product2_)

プライマリキーで比較されていますね。

4.6.9 Like Expressions

これもSQLと同じかな。

select p from Product p where p.name like 'お%'

エスケープ文字の指定もできるようです。

4.6.10 Null Comparison Expression

これも...

select p from Product p where p.price IS NULL

4.6.11 Empty Collection Comparison Expression

これはEJB QL独特。パス式であらわされるコレクションに要素があるかどうか検査するそうです。

select distinct o from Order o where o.lineItems is empty

SQLが気になります。こうなりました。

Hibernate: select distinct order0_.id as id3_, order0_.shippingAddress_id as shipping2_3_, order0_.billingAddress_id as billingA3_3_ 
from ODR order0_ where  not (exists (select lineitems1_.id from LineItem lineitems1_ where order0_.id=lineitems1_.order_id))

おぉ。

4.6.12 Collection Member Expressions

値がコレクションのメンバかどうか検査するものらしい。パラメータでLineItemエンティティのインスタンスを渡してみました。

select distinct o from Order o where :lineItem member of o.lineItems 

上記EJB QLからは次のSQLが生成されています。

Hibernate: select distinct order0_.id as id3_, order0_.shippingAddress_id as shipping2_3_, order0_.billingAddress_id as billingA3_3_ 
from ODR order0_ where ? in (select lineitems1_.id from LineItem lineitems1_ where order0_.id=lineitems1_.order_id)

プライマリキーで比較されていますね。
ofをつけるのはオプションらしいのですが、はずしたら動きませんでしたよ...。

4.6.13 Exists Expressions

これはSQLと同じですね。相関サブクエリも使えます。

select p from Product p where exists(select l from LineItem l where l.product = p and l.quantity > 15)

4.6.14 All or Any Expressions

ALLとかANYとかSOMEが使えます。これもSQLと同じだと思います。

select p from Product p where p.price > all (select p2.price from LineItem l inner join l.product p2 where p2 = p and l.quantity > 15)

あれ、動かない...。もしかしてHSQDB1.7.2ってALLとかANYってサポートしてない?またはこのクエリがまちがっているか...

4.6.15 Subqueries

サブクエリがつかえます。今までにもう何回かつかっています。
このリリースではWHERE節とGROUP BY節でのサブクエリがサポートされているとか。FROM節でのサブクエリは後のリリースでサポートされるらしいです。

4.6.16 Functional Expressions

組み込みの関数がつかえるらしいです。

4.6.16.1 String Expressions
まずStringに関する関数。使える関数はつぎのものです。

  • CONCAT
  • SUBSTRING
  • TRIM
  • LOWER
  • UPPER

SUBSTRINGを使ってみます。

select p from Product p where substring(p.name, 1, 1) = 'お'

4.6.16.2 Arithmetic Expressions
次に算術に関する関数。使える関数は4つです。

  • ABS
  • SQRT
  • MOD
  • SIZE

SIZEを使ってみます。SIZE関数はコレクションの要素数を返すらしいです。

select distinct o from Order o where size(o.lineItems) > 2)

SQLはこうなりました。

Hibernate: select distinct order0_.id as id3_, order0_.shippingAddress_id as shipping2_3_, order0_.billingAddress_id as billingA3_3_ 
from ODR order0_ where (select count(lineitems1_.order_id) from LineItem lineitems1_ where order0_.id=lineitems1_.order_id)>2

SIZE関数を使ったのと同じクエリは次のようにしても動きました。

select distinct o from Order o where o.lineItems.size > 2

でもこれはPersistent APIの標準なのかはわかりません。HibernateのEntity Managerのドキュメントを見るとSIZE関数を使ったほうがHQL specificでsizeプロパティをつかうのが標準と読めるのですが...、ドキュメントのまちがい?SIZE関数が標準でsizeプロパティがHQL specificといいたいのかも。

4.6 Conditional Expressionsおわりです。Chapter 4、あとまだ15ページくらいあるなぁ。でもでも、後ろのほうはBNFだから省けそうだし、もうすでに使ったことがある例が載ったページがあるし実質10ページくらいかも。