规格
JPA 2 引入了一个条件 API,您可以使用该 API 以编程方式构建查询。通过编写criteria
中,您可以为域类定义查询的 where 子句。再退一步,这些标准可以被视为 JPA 标准 API 约束所描述的实体的谓词。
Spring Data JPA 采用了 Eric Evans 的书《Domain Driven Design》中的规范概念,遵循相同的语义,并提供了一个 API 来使用 JPA 标准 API 定义此类规范。要支持规范,您可以使用JpaSpecificationExecutor
接口,如下所示:
public interface CustomerRepository extends CrudRepository<Customer, Long>, JpaSpecificationExecutor<Customer> {
…
}
附加接口具有允许您以多种方式运行规范的方法。例如,findAll
method 返回与规范匹配的所有实体,如以下示例所示:
List<T> findAll(Specification<T> spec);
这Specification
接口定义如下:
public interface Specification<T> {
Predicate toPredicate(Root<T> root, CriteriaQuery<?> query,
CriteriaBuilder builder);
}
规范可以很容易地用于在实体之上构建一组可扩展的谓词,然后可以将其组合和使用JpaRepository
而无需为每个需要的组合声明 Query (method),如以下示例所示:
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_
type 是使用 JPA Metamodel 生成器生成的元模型类型(有关示例,请参见 Hibernate 实现的文档)。
所以表达式Customer_.createdAt
,则假定Customer
具有createdAt
type 为Date
.
除此之外,我们还在业务需求抽象级别上表达了一些标准,并创建了可执行文件Specifications
.
因此,客户端可能会使用Specification
如下:
List<Customer> customers = customerRepository.findAll(isLongTermCustomer());
为什么不为此类数据访问创建查询呢?使用单个Specification
与普通查询声明相比,它不会获得很多好处。当您将规范组合起来创建新的Specification
对象。您可以通过Specification
我们提供 to build 类似于以下内容的表达式:
MonetaryAmount amount = new MonetaryAmount(200.0, Currencies.DOLLAR);
List<Customer> customers = customerRepository.findAll(
isLongTermCustomer().or(hasSalesOfMoreThan(amount)));
Specification
提供一些 “glue-code” 默认方法来链接和组合Specification
实例。这些方法允许您通过创建新的Specification
实现,并将它们与现有的实现相结合。
在 JPA 2.1 中,CriteriaBuilder
API 引入CriteriaDelete
.这是通过以下方式提供的JpaSpecificationExecutor’s `delete(Specification)
应用程序接口。
Specification
以删除条目。Specification<User> ageLessThan18 = (root, query, cb) -> cb.lessThan(root.get("age").as(Integer.class), 18)
userRepository.delete(ageLessThan18);
这Specification
构建一个标准,其中age
field (cast as an integer) 小于18
.
传递给userRepository
,它将使用 JPA 的CriteriaDelete
功能以生成正确的DELETE
操作。
然后,它返回已删除的实体数。