NetBeans6.8 BetaでJPA2.0を試してみる。 - その4

 今回はMetamodelAPIを使ってSQLでいうところのWhere句とかそこらへんとかやってみます。

んで、いろいろやる前にCriteriaBuilder、CriteriaQueryおよびRoot作成までの処理。

        CriteriaBuilder qb = em.getCriteriaBuilder();
        CriteriaQuery<Customer> cq = qb.createQuery(Customer.class);
        Root<Customer> r = cq.from(Customer.class);

ちょっといろいろ試してみたところ、ここまではローカル変数として定義しないといろいろやれないっぽいです。

なので、以下いろいろソース書いていきますが、それらのソースでは前提としてすでにこれらの変数が定義されているものとして見てください。
使うEntityとMetamodelは前回のCustomerとCustomer_ということでよろしくお願いします。


条件を指定した絞込み

SQLで言うところの"="を使った絞込み

        cq.select(r).
                where(qb.equal(r.get(Customer_.city), "Detroit"));

上記の文を日本語的にすると「Rootに対してselectし、whereメソッドを使って条件の絞込みを行い、その中ではQriteriaBuilderによるイコール(equal)判定の処理を実行する。」といったところでしょうか。なんかかなり変な日本語っぽい・・(´д`)

ちなみに下記はコンパイルエラーになります。

        //DiscountCode_はDiscountエンティティに対するMetamodel
        cq.select(r).
                where(qb.equal(r.get(DiscountCode_.rate), "Detroit"));

このようにMetamodelを利用してCriteriaAPIを使うと型に対して安全にプログラミングができます。JPQLを文字列でゴリゴリ書いていく場合はどうしても実行するまで構文エラー(タイプミス等)に気づきにくいですが、MetamodelとCriteriaAPIを使用することでそれが無くなる上にIDEのコード補完機能でサクサクJPQLのプログラミングができるようになります*1

ただし、CustomerのプロパティであるcityはString型ですが、それに対する値(上記でいうと「"Detroit"」)に対する型の厳密性はequal形のメソッドに関してのみ無いもようです(between等はフィールドの型と同じか、そのサブクラスでなければならない)

SQLで言うところの"!="を使った絞込み

        cq.select(r).
                where(qb.notEqual(r.get(Customer_.city), "Detroit"));

こんな感じでCriteriaBuilderのメソッドには条件指定のメソッドがたくさんあります。下記によく使われるであろうメソッドを1部列挙してみます。

メソッド 意味
isNull nullでの絞込み
isNotNull null以外での絞込み
isTrue trueでの絞込み*2
isFalse falseでの絞込み*3
between 指定された範囲による絞込み(SQL(JPQL)のbetweenと等価)
like 部分一致による絞込み*4

複数条件指定

whereメソッド内に複数の条件を指定していきます(whereメソッドは可変長引数になっています)

    cq.select(r).
        where(qb.between(r.get(Customer_.customerId), 1,10),
              qb.equal(r.get(Customer_.city), "Dearborn"));

上記は下記JQPLと等価です。

SELECT 
   c 
FROM 
   Customer c 
WHERE 
   c.customerId BETWEEN 1 AND 1000 AND 
   c.city = "Dearborn"

おまけ

例えば下記のようなCriteriaがあった場合

    cq.select(r).
            where(
                  qb.between(r.get(Customer_.customerId), 1,10000),
                  qb.or(
                        qb.equal(r.get(Customer_.city), "Dearborn"),
                        qb.like(r.get(Customer_.city), "%New%")),
                  qb.isNotNull(r.get(Customer_.addressline1)));

Customer_をstatic importしておけば下記のように多少見やすくなるかなと思いますがどうでしょうか??

    cq.select(r).
            where(
                  qb.between(r.get(customerId), 1,10000),
                  qb.or(
                        qb.equal(r.get(city), "Dearborn"),
                        qb.like(r.get(city), "%New%")),
                  qb.isNotNull(r.get(addressline1)));

ちなみに上記は下記JPQLと等価です。

SELECT 
   c 
FROM 
   Customer c 
WHERE 
   c.customerId BETWEEN 1 AND 10000 AND 
  (c.city = "Dearborn" OR c.city LIKE "%New%" ) AND
  c.addressline1 IS NOT NULL

*1:SQLっぽさは減りますが

*2:対象のエンティティのフィールドの型がBooleanでなければならない

*3:対象のエンティティのフィールドの型がBooleanでなければならない

*4:SQLのLIKE構文と等価