Spring Data JPA で遊んでみる 〜その10〜

Spring Data JPA では Querydsl なるものがサポートされています。
http://www.querydsl.com/

色々機能があるみたいですが、カンタに言うとEntityクラスからAPTでメタ情報のクラスを生成して、そのクラスを利用してタイプセーフで流れるようにクエリが書けるようなものです。s2jdbcチックな感じです。

APTなので若干セットアップがかったるいですが。
まずpomにライブラリの追加。

    <!-- Query Dsl -->
    <dependency>
      <groupId>com.mysema.querydsl</groupId>
      <artifactId>querydsl-jpa</artifactId>
      <version>2.2.3</version>
    </dependency>
    <dependency>
      <groupId>com.mysema.querydsl</groupId>
      <artifactId>querydsl-apt</artifactId>
      <version>2.2.3</version>
      <scope>provided</scope>
    </dependency>

mavenでaptを実行するようにしておきます。

      <plugin>
        <groupId>com.mysema.maven</groupId>
        <artifactId>maven-apt-plugin</artifactId>
        <version>1.0</version>
        <executions>
          <execution>
            <phase>generate-sources</phase>
            <goals>
              <goal>process</goal>
            </goals>
            <configuration>
              <outputDirectory>target/generated-sources</outputDirectory>
              <processor>com.mysema.query.apt.jpa.JPAAnnotationProcessor</processor>
            </configuration>
          </execution>
        </executions>
      </plugin>

ついでにソースを追加するあれをいれておくとよい。

      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>build-helper-maven-plugin</artifactId>
        <executions>
          <execution>
            <id>add-source</id>
            <phase>compile</phase>
            <goals>
              <goal>add-source</goal>
            </goals>
            <configuration>
              <sources>
                <source>target/generated-sources</source>
              </sources>
            </configuration>
          </execution>
        </executions>
      </plugin>

最近はm2eの仕組みが色々と変わってEclipseで連携する場合は色々とめんどい。。。ので書きません。

良い感じにソースコードが自動生成されるようになったらリポジトリにQueryDslPredicateExecutorを継承させます。

public interface EmpRepository extends QueryDslPredicateExecutor<Emp>

QueryDslPredicateExecutorに定義されているのは以下のメソッド

T findOne(Predicate predicate);
Iterable<T> findAll(Predicate predicate);
Iterable<T> findAll(Predicate predicate, OrderSpecifier<?>... orders);
Page<T> findAll(Predicate predicate, Pageable pageable);
long count(Predicate predicate);

使うときは自動生成されたメタクラスを使用しつつクエリを組み立てて食わせます。

@Test
public void Querydslを使う() throws Exception {
    Iterable<Emp> emps = repository.findAll(QEmp.emp.id.lt(9L).and(QEmp.emp.dept.id.eq(1L)));
}

サンプルはこんなん
https://github.com/yamkazu/springdata-jpa-example/tree/querydsl

Spring Data JPA で遊んでみる 〜その9〜

昨日のSpecificationの続きでSpecificationを複数組み合わせて使う際に便利な、Specificationsというヘルパークラスが用意されている。

public class Specifications<T> implements Specification<T> {
  private final Specification<T> spec;
  private Specifications(Specification<T> spec) {...}
  public static <T> Specifications<T> where(Specification<T> spec) {...}
  public Specifications<T> and(final Specification<T> other) {...}
  public Specifications<T> or(final Specification<T> other) {...}
  public static <T> Specifications<T> not(final Specification<T> spec) {...}
  public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder builder) {...}
}

幾つかメソッドが定義されていますがwhereを基点としてandとかorとかでつなげていく感じ。

例えば2つSpecがあるとして

public class EmpSpecifications {
    public static Specification<Emp> idLessThanOrEqualTo(final Long id) {
        return new Specification<Emp>() {
            @Override
            public Predicate toPredicate(Root<Emp> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                return cb.lessThanOrEqualTo(root.get(Emp_.id), id);
            }
        };
    }

    public static Specification<Emp> hasDept(final Dept dept) {
        return new Specification<Emp>() {
            @Override
            public Predicate toPredicate(Root<Emp> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                return cb.equal(root.get(Emp_.dept), dept);
            }
        };
    }
}

これを組み合わせて使う場合は

List<Emp> emps = repository.findAll(where(idLessThanOrEqualTo(9L)).and(hasDept(Dept.of(1L))));

とかいうふうに使えます。

サンプルこのへん。
https://github.com/yamkazu/springdata-jpa-example/tree/complexspec

Spring Data JPA で遊んでみる 〜その11〜

ここまで紹介てきたクエリの発火方法で、恐らくほとんどのことができるんじゃないかと思います。ただ、どうしても自分で実装を持ちたくなるような時もあります。

Spring Data JPA では定義したリポジトリのインタフェースに対し、独自の拡張クラスを作成することができます。

まず拡張したいメソッドを定義したインタフェースを用意します。

public interface EmpRepositoryCustom {
    String echo(String message);
}

拡張メソッドだけを定義したインタフェースを個別に起こしているところに注意してください。

これをリポジトリのインタフェースに継承させます。

public interface EmpRepository extends EmpRepositoryCustom {

インタフェースの準備はこれで終わりです。実装くらすはEmpRepositoryCustomを実装しつつクラス名をリポジトリ名+Implとなるようにします。このルールは変えられますし、独自にSpringのBeanとして個別に定義することもできます。詳しくはマニュアルを参照してください。

実装クラスはこんなんです。

public class EmpRepositoryImpl implements EmpRepositoryCustom {
    @Override
    public String echo(String message) {
        return message;
    }
}

ちょっとインタフェースの定義が、独自メソッドだけ切り出してインタフェースを別に起こさないといけないなど、手間な感じが最初はしたのですが、一貫して基底となるインタフェースで、他のインタフェースを継承することで機能拡張が行えるというスタイルな感じがして、これはこれでmixinポイ感じで統一感があるのかなぁと思いました。これはJpaRepositoryを継承すればJPAの機能が、JpaSpecificationExecutorを継承すればSpecificationの機能が、QueryDslPredicateExecutorを継承すればQueryDslの機能が、独自のインタフェースを継承すれば独自の機能がという感じです。

サンプル
https://github.com/yamkazu/springdata-jpa-example/tree/customimpl

Spring Data JPA で遊んでみる まとめ

いろいろ紹介してきましたが、全部以下に書いてあるので、ざっと目を通すといいかなぁと思います。

http://static.springsource.org/spring-data/data-jpa/docs/current/reference/html/#repositories.custom-implementations

色々触ってみた感想は、Spring で JPA 使うなら1番目の選択肢として考えていいじゃないかと思います。

メソッドだけでクエリを定義できるといった高級な機能から、独自実装ももてるといった、幅広いカスタマイズ性も持ちあわせており、守備範囲が広く良く出来てるなぁといった印象です。

では、そんな感じで。