此版本仍在开发中,尚未被视为稳定版。如需使用最新的稳定版本,请访问 Spring Data JPA 4.0.4spring-doc.cadn.net.cn

规范

JPA 2 引入了一个 Criteria API,可用于以编程方式构建查询。通过编写一个 criteria,你可以为某个领域类定义查询的 WHERE 子句。更进一步来说,这些条件可被视为对实体的一种谓词,该谓词由 JPA Criteria API 的约束条件所描述。spring-doc.cadn.net.cn

Spring Data JPA 采纳了 Eric Evans 在其著作《领域驱动设计》(Domain Driven Design)中提出的“规约”(specification)概念,遵循相同的语义,并提供了基于 JPA Criteria API 来定义此类规约的接口。为了支持规约,您可以将自己的仓库接口扩展为 JpaSpecificationExecutor 接口,如下所示:spring-doc.cadn.net.cn

public interface CustomerRepository extends CrudRepository<Customer, Long>, JpaSpecificationExecutor<Customer> {
 …
}

该附加接口提供了多种方法,允许您以不同方式执行规格(Specification)。例如,findAll 方法会返回所有匹配该规格的实体,如下例所示:spring-doc.cadn.net.cn

List<T> findAll(Specification<T> spec);

Specification 接口定义如下:spring-doc.cadn.net.cn

public interface Specification<T> {
  Predicate toPredicate(Root<T> root, CriteriaQuery<?> query,
            CriteriaBuilder builder);
}

规范(Specifications)可以轻松地用于在实体之上构建一组可扩展的谓词,然后这些谓词可以进行组合,并与 JpaRepository 一起使用,而无需为每一种所需的组合都声明一个查询(方法),如下例所示:spring-doc.cadn.net.cn

示例 1. 客户的规格说明
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 具有一个类型为 createdAtDate 属性。 除此之外,我们还在业务需求抽象层面上表达了一些条件,并创建了可执行的 Specifications。 因此,客户端可以按如下方式使用 Specificationspring-doc.cadn.net.cn

示例 2. 使用一个简单的 Specification
List<Customer> customers = customerRepository.findAll(isLongTermCustomer());

为什么不为此类数据访问创建一个查询呢?仅使用单个 Specification 相较于直接声明普通查询并没有带来太多优势。Specification 的强大之处在于将它们组合起来,从而创建新的 Specification 对象。你可以通过我们提供的 3 接口中的默认方法来实现这一点,以构建类似于以下的表达式:spring-doc.cadn.net.cn

示例 3. 组合规格
MonetaryAmount amount = new MonetaryAmount(200.0, Currencies.DOLLAR);
List<Customer> customers = customerRepository.findAll(
  isLongTermCustomer().or(hasSalesOfMoreThan(amount)));

Specification 提供了一些“粘合代码”默认方法,用于链式调用和组合 Specification 实例。这些方法允许你通过创建新的 Specification 实现,并将其与已有的实现进行组合,从而扩展你的数据访问层。spring-doc.cadn.net.cn

并且在 JPA 2.1 中,CriteriaBuilder API 引入了 CriteriaDelete。该功能通过 JpaSpecificationExecutor’s `delete(Specification)3 API 提供。spring-doc.cadn.net.cn

示例4. 使用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 操作。 然后返回已删除的实体数量。spring-doc.cadn.net.cn