QuerydslでJPAが思ったよりも捗る

先日、 [twitter:@seratch]さんから教えていただいたQuerydslがすごい良かったので記事として書きます。

http://www.querydsl.com/

JPAにおける課題

JPAではJPQLとCriteriaという二つのクエリ記述言語があります。
しかしながら、それぞれ使い勝手という意味では難のあるものでした。

JPQL

JPQLは以下のような、SQLライクなクエリ記述言語です。

select new com.github.megascus.EmployeeBean(e.code, e.name, e.age) from Employee as e where name like 's%'

SQLライクに記述することができるため、SQLが理解できる人にとっては理解しやすいという利点があります。
しかしながら、JPQL自体はただの文字列で定義する必要があります。
そのため、IDEの恩恵を受けにくく、JPQLを書いたもののシンタックスエラーのため動かないという場合があります。

Criteria

JPQLにはタイプセーフではないという欠点がありましたが、それを解消するために考え出されたのがCriteriaです。

CriteriaQuery query = builder.createQuery();
Root<Person> men = query.from( Person.class );
Root<Person> women = query.from( Person.class );
Predicate menRestriction = builder.and(
    builder.equal( men.get( Person_.gender ), Gender.MALE ),
    builder.equal( men.get( Person_.relationshipStatus ),RelationshipStatus.SINGLE ));
Predicate womenRestriction = builder.and(
    builder.equal( women.get( Person_.gender ), Gender.FEMALE ),
    builder.equal( women.get( Person_.relationshipStatus ),RelationshipStatus.SINGLE ));
query.where( builder.and( menRestriction, womenRestriction ) );

しかしながら、記述方法が独特かつ冗長であり、一般の人間には理解しがたいものでした。*1

よって、実際に使いこなせている人は少なくとも自分の周りには居ません。


以上のように、JPAの標準の二つのクエリ記述言語はどちらも一長一短のあるものとなっています。

JPAのクエリ記述言語を改良したS2JDBC

少し話は変わりますが、JPAのクエリ記述言語を使いやすくするために生まれた、S2JDBCというクエリ記述言語があります。*2

http://s2container.seasar.org/2.4/ja/s2jdbc_abstract.html

以下のように、Javaプログラミングの一部としてクエリを記述することができます。

List<Employee> results = jdbcManager.from(Employee.class)
                             .join("department")
                             .where("id in (? , ?)", 11, 22)
                             .orderBy("name")
                             .getResultList();

Criteriaに比べるとはるかに可読性が高く、JPQLに比べるとはるかにタイプセーフになっています。
ただし、上の例ですと、"department"や、"id in (? , ?)"といったところはまだまだタイプセーフではなく、シンタックスエラーにより実際に動作させても動かない可能性というのは残っています。

S2JDBCのように可読性が高く、完全にタイプセーフになったQuerydsl

さて、ここでやっとQuerydslが出てきます。
QuerydslになるとS2JDBCではまだまだタイプセーフではなかった場所が完全にタイプセーフになります。

QCustomer customer = QCustomer.customer;
query.from(customer)
    .orderBy(customer.lastName.asc(), customer.firstName.desc())
    .list(customer);

S2JDBCではエンティティはともかく、プロパティに関しては文字列で指定するという制限がありましたが、Querydslではアノテーションプロセッサーを使い、エンティティのメタデータを作成ます。
メタデータでプログラミングをすることでタイプセーフな状態を確保するという戦略をとっています。
完全なるS2JDBCの後継と言っても良いのでは無いでしょうか。*3

ちなみに、先日書いたブログのJPQLをQuerydslで書き直すと以下のようになります。

  • JPQL
select new megascus.Products(p1, p2) 
       from Product as p1, Product as p2 
       where p1.name = p2.name
  • Querydsl
query.from(p1, p2)
     .where(p1.name.eq(p2.name))
     .list(ConstructorExpression.create(Products.class, p1, p2));

JPQLのコンストラクタ式ではパッケージ名からクエリに含める必要があるためリファクタリングの天敵となる場合がありましたが、Querydslではそのようなこともありません。
非常にオススメです。

*1:元になったHibernate Criteriaは理解しやすかったのですが、どうしてこうなった・・・・・

*2:ただし、S2JDBCが生まれた時にはCriteriaはありませんでした。

*3:もちろん製作者は違いますが。