Kuina-Dao 開発記 Criteria
Kuina-Dao はインタフェースに定義されたメソッドのシグネチャと実引数から動的に JPQL を作成して実行します.
でもでも,JPA には Criteria がないため,JPQL を組み立てるのが面倒です.
そんなわけで (どんなわけで?),Kuina-Dao のインフラとして Criteria を実装しました.
Kuina-Dao Criteria を使うには,まずは
import static org.seasar.kuina.dao.criteria.CriteriaOperations.*;
します.
Eclipse の場合は「Window」−「Preferences」から「Java」−「Code Style」−「Organize Import」を開いて「New Static...」で上のクラスを登録しておきます.
それでも Eclipse (3.1) は static import の扱いがイマイチで全然快適じゃないんですけどね.
ともあれ (JW),Kuina-Dao Criteria を使った一番簡単な問い合わせは次のようになります.
List<Employee> list = select().from(Employee.class).getResultList(em);
スッキリしてて簡単でしょ? (^^;
このように select()
に引数を指定しない場合は from()
句に指定したエンティティが選択されます.
これを明示的に指定すると
List<Employee> list = select("employee").from(Employee.class).getResultList(em);
となります.
ちなみに,from()
にクラスだけ指定しているので,エンティティクラス名を decapitalize したものが alias になります.
それも明示的に指定すると
List<Employee> list = select("e").from(Employee.class, "e").getResultList(em);
となります.
select()
は実は可変長引数を持つので,複数の選択リストを並べることができます.
List<Object[]> list = select("e.name", "e.email").from(Employee.class, "e").getResultList(em);
結果リストから重複を除くには selectDistinct()
を使います.
List<String> list = selectDistinct("e.bloodType").from(Employee.class, "e").getResultList(em);
結合も簡単.
from()
も可変長引数を受け取ります.その場合はクロス結合になります.
List<Object[]> list = select().from(Employee.class, Department.class).getResultList(em);
可変長引数の from()
で alias を指定するには alias()
を使います.
List<Object[]> list = select() .from(alias(Employee.class, "e"), alias(Department.class, "d")) .getResultList(em);
内部結合になると少し複雑な書き方になっちゃいます.
List<Object[]> list = select() .from(join(Employee.class).inner("employee.belongTo").inner("belongTo.department")) .getResultList(em);
alias を使うとこう.
List<Object[]> list = select() .from(join(Employee.class, "e").inner("e.belongTo", "b").inner("b.department", "d")) .getResultList(em);
いずれの場合も,結合した 3 つのエンティティの配列からなるリストが返されます.
FETCH JOIN も使えます.
List<Employee> list = select() .from(join(Employee.class, "e").innerFetch("e.belongTo", "b").innerFetch("b.department", "d")) .getResultList(em);
この場合は Employee
だけが返されます.
外部結合も同様.
List<Product> list = selectDistinct() .from(join(Product.class, "p").leftFetch("p.sales")) .getResultList(em);
この場合は selectDistinct()
しないと SQL の結果セットの行数と同じ長さのリストが返ってきます.
Hibernate の動きがそのまま JPA 仕様になったらしい.
Linda にそれはやめてと言ったんだけどなぁ.その時「それはよくないね」みたいに言ってたんだけどなぁ.
でもでも,「Pro EJB 3 Java Persistence API (isbn:1590596455)」を見ると,どうやら TopLink でも distinct しなきゃいけないみたいだし.
しくしくしく.
WHERE 句もまぁ,似たようなものです.
List<Employee> list = select().from(Employee.class) .where(eq("employee.bloodType", literal("AB"))) .getResultList(em);
ここでは血液型が AB の従業員を選択しています.
ちょっと悩ましいのは,String
引数の扱い.
上の場合,eq()
メソッドは 2 つの引数を受け取るのですが,現在そこに String
を渡すと employee.bloodType
のような Path Expression として解釈しています.
そのため,'AB'
のようなリテラルを指定するには literal()
を介する必要があります.
油断すると忘れそうなのがイマイチなのですが,String
をリテラルだと解釈すると Path Expression の時に明示的に path()
とか介さないといけなくなるのでどっちもどっちなんですよね.
いっそ String
はコンパイルエラーになるようにしようかとも思ったのですが,それはそれで面倒になるし.
この辺はまだ悩みながらやっているので,いいアイディアあったらお願いします.
where()
も可変長引数を受け取ります.
List<Employee> list = select().from(Employee.class, "e") .where(eq("e.height", 150), eq("e.weight", 45)) .getResultList(em);
このように並べると AND で繋がれます.
OR を使う場合は
List<Employee> list = select().from(Employee.class, "e") .where(or(lt("e.weight", 45), gt("e.weight", 70))) .getResultList(em);
とします.and()
もあります.いずれも可変長引数を受け取ります.
基本的な演算子やファンクションなどはだいたい揃ってます.
集計関数や GROUP BY,HAVING,ORDER BY もサポート.
List<Object[]> list = select("p.id", count("c")) .from(join(Customer.class, "c").inner("c.prefectural", "p")) .groupby("p.id") .having(ge(count("c"), 3)) .orderby("p.id") .getResultList(em);
顧客が 3 件以上ある県とその顧客数を県の ID 順に取得します.
ここで orderby()
を count()
順にすることができない気のせいが.
JPQL の BNF 的にできるとは思えない...
できそうにないといえば,
List<Integer> list = select(add("e.height", 10)).from(Employee.class, "e").getResultList();
JPQL でいうと
SELECT e.height + 10 FROM Employee AS e
みたいに SELECT 句に集約関数以外の計算式を書くことも BNF 的にできない気のせいが...
Hibernate ではできちゃうけど (苦笑).
「Pro EJB 3 Java Persistence API (isbn:1590596455)」の例には出てこないので,TopLink に配慮してできなくなってる?
ともあれ (JW),JPQL はまだまだ拡張されて完全な SQL に近づいていくんだろうなぁ (苦笑).
といった感じで,Criteria そのものはいい感じになったかなぁ,と.
それもこれも static import,可変長引数,Generics などなど,Tiger 様のおかげです.
でもでも,おかげで CriteriaOperations
は超巨大になってしまった...
今現在でも 1500 行くらいあるよ?
心より恥じる.
まだ対応ができていないのは,大きなところでは副問い合わせ.
それに関連して EXISTS とかその辺.
おいおい実装していきます.