规格

JPA 2 引入了一个条件 API,您可以使用该 API 以编程方式构建查询。通过编写 ,您可以为域类定义查询的 where 子句。再退一步,这些标准可以被视为 JPA 标准 API 约束所描述的实体的谓词。criteriaspring-doc.cn

Spring Data JPA 采用了 Eric Evans 的书《Domain Driven Design》中的规范概念,遵循相同的语义,并提供了一个 API 来使用 JPA 标准 API 定义此类规范。要支持规范,您可以使用该接口扩展存储库接口,如下所示:JpaSpecificationExecutorspring-doc.cn

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

附加接口具有允许您以多种方式运行规范的方法。例如,该方法返回与规范匹配的所有实体,如以下示例所示:findAllspring-doc.cn

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

接口定义如下:Specificationspring-doc.cn

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

规范可以很容易地用于在实体之上构建一组可扩展的谓词,然后可以组合和使用这些谓词,而无需为每个需要的组合声明查询(方法),如以下示例所示:JpaRepositoryspring-doc.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
    };
  }
}

该类型是使用 JPA Metamodel 生成器生成的元模型类型(有关示例,请参见 Hibernate 实现的文档)。 因此,表达式 , 假定 具有 类型的属性。 除此之外,我们还在业务需求抽象级别上表达了一些标准,并创建了可执行的 . 因此,客户端可能会按如下方式使用 a:Customer_Customer_.createdAtCustomercreatedAtDateSpecificationsSpecificationspring-doc.cn

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

为什么不为此类数据访问创建查询呢?与普通查询声明相比,使用 single 并没有获得很多好处。当您将规范组合起来创建新对象时,它们的力量确实会大放异彩。您可以通过我们提供的默认方法来构建类似于以下内容的表达式来实现此目的:SpecificationSpecificationSpecificationspring-doc.cn

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

Specification提供一些 “glue-code” 默认方法来链接和组合实例。这些方法允许您通过创建新的实施并将其与现有实施相结合来扩展数据访问层。SpecificationSpecificationspring-doc.cn

在 JPA 2.1 中,API 引入了 .这是通过 API 提供的。CriteriaBuilderCriteriaDeleteJpaSpecificationExecutor’s `delete(Specification)spring-doc.cn

示例 4.使用 a 删除条目。Specification
Specification<User> ageLessThan18 = (root, query, cb) -> cb.lessThan(root.get("age").as(Integer.class), 18)

userRepository.delete(ageLessThan18);

这建立了一个条件,其中字段(转换为整数)小于 。 传递给 ,它将使用 JPA 的功能来生成正确的操作。 然后,它返回已删除的实体数。Specificationage18userRepositoryCriteriaDeleteDELETEspring-doc.cn