Specification
JPA 2 は、クエリをプログラムで作成するために使用できる条件 API を導入しています。criteria
を記述することにより、ドメインクラスのクエリの where 句を定義します。別のステップに戻ると、これらの条件は、JPA 条件 API 制約によって記述されたエンティティに関する述語と見なすことができます。
Spring Data JPA は、同じセマンティクスに従い、JPA 条件 API を使用してそのような仕様を定義する API を提供する、エリックエバンスの著書 "Domain Driven Design [Amazon] " から仕様の概念を取り入れています。次のように、仕様をサポートするために、JpaSpecificationExecutor
インターフェースを使用してリポジトリインターフェースを継承できます。
public interface CustomerRepository extends CrudRepository<Customer, Long>, JpaSpecificationExecutor<Customer> {
…
}
追加のインターフェースには、さまざまな方法で仕様を実行できるメソッドがあります。例: 次の例に示すように、findAll
メソッドは仕様に一致するすべてのエンティティを返します。
List<T> findAll(Specification<T> spec);
Specification
インターフェースは次のように定義されます。
public interface Specification<T> {
Predicate toPredicate(Root<T> root, CriteriaQuery<?> query,
CriteriaBuilder builder);
}
次の例に示すように、仕様を簡単に使用して、エンティティの上に拡張可能な一連の述語を構築し、JpaRepository
と組み合わせて使用し、必要な組み合わせごとにクエリ(メソッド)を宣言する必要なしに使用できます。
public class CustomerSpecs {
public static Specification<Customer> isLongTermCustomer() {
return (root, query, builder) -> {
LocalDate date = LocalDate.now().minusYears(2);
return builder.lessThan(root.get(Customer_.createdAt), date);
};
}
public static Specification<Customer> hasSalesOfMoreThan(MonetaryAmount value) {
return (root, query, builder) -> {
// build query here
};
}
}
Customer_
型は、JPA メタモデルジェネレーターを使用して生成されたメタモデル型です(Hibernate 実装のドキュメントの例 (英語) を参照)。式 Customer_.createdAt
は、Customer
が型 Date
の createdAt
属性を持っていることを前提としています。さらに、ビジネス要件の抽象化レベルでいくつかの条件を表現し、実行可能な Specifications
を作成しました。クライアントは次のように Specification
を使用する可能性があります。
List<Customer> customers = customerRepository.findAll(isLongTermCustomer());
この種のデータアクセスのクエリを作成してみませんか? 単一の Specification
を使用しても、単純なクエリ宣言よりも大きな利点は得られません。仕様を組み合わせて新しい Specification
オブジェクトを作成すると、仕様の威力が本当に発揮されます。これは、次のような式を作成するために提供されている Specification
のデフォルトメソッドを使用して実現できます。
MonetaryAmount amount = new MonetaryAmount(200.0, Currencies.DOLLAR);
List<Customer> customers = customerRepository.findAll(
isLongTermCustomer().or(hasSalesOfMoreThan(amount)));
Specification
は、チェーンにいくつかの「グルーコード」デフォルトメソッドを提供し、Specification
インスタンスを結合します。これらのメソッドを使用すると、新しい Specification
実装を作成し、既存の実装と組み合わせることにより、データアクセスレイヤーを継承できます。
また、JPA 2.1 では、CriteriaBuilder
API によって CriteriaDelete
が導入されました。これは、JpaSpecificationExecutor’s `delete(Specification)
API を介して提供されます。
Specification
を使用してエントリを削除します。Specification<User> ageLessThan18 = (root, query, cb) -> cb.lessThan(root.get("age").as(Integer.class), 18)
userRepository.delete(ageLessThan18);
Specification
は、age
フィールド(整数としてキャスト)が 18
よりも小さい条件を構築します。userRepository
に渡され、JPA の CriteriaDelete
機能を使用して、適切な DELETE
操作を生成します。次に、削除されたエンティティの数を返します。