侧边栏壁纸
  • 累计撰写 793 篇文章
  • 累计创建 1 个标签
  • 累计收到 1 条评论
标签搜索

目 录CONTENT

文章目录

Specifications

Dettan
2022-03-04 / 0 评论 / 0 点赞 / 84 阅读 / 415 字
温馨提示:
本文最后更新于 2022-07-23,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。
💡
可复用的查询条件
To be able to define reusable Predicates we introduced the Specification interface that is derived from concepts introduced in Eric Evans’ Domain Driven Design book. It defines a specification as a predicate over an entity which is exactly what our Specification interface represents. The actually only consists of a single method:
public interface Specification<T> {
  Predicate toPredicate(Root<T> root, CriteriaQuery query, CriteriaBuilder cb);
}COPY
so we can now easily use a helper class like this:
public CustomerSpecifications {

  public static Specification<Customer> customerHasBirthday() {
    return new Specification<Customer> {
      public Predicate toPredicate(Root<T> root, CriteriaQuery query, CriteriaBuilder cb) {
        return cb.equal(root.get(Customer_.birthday), today);
      }
    };
  }

  public static Specification<Customer> isLongTermCustomer() {
    return new Specification<Customer> {
      public Predicate toPredicate(Root<T> root, CriteriaQuery query, CriteriaBuilder cb) {
        return cb.lessThan(root.get(Customer_.createdAt), new LocalDate.minusYears(2));
      }
    };
  }
}COPY
Admittedly, not the most beautiful code in the world but it serves our initial requirement quite nicely: we can refer to a set of atomic specifications. The next question is: how will we execute these specifications? To do so, you simply extend JpaSpecificationExecutor in your repository interface and thus “pull in” an API to execute Specifications:
public interface CustomerRepository extends JpaRepository<Customer>, JpaSpecificationExecutor {
  // Your query methods here
}COPY
A client can now do:
customerRepository.findAll(hasBirthday());
customerRepository.findAll(isLongTermCustomer());COPY
The basic repository implementation will prepare the CriteriaQueryRoot and CriteriaBuilder for you, apply the Predicate created by the given Specification and execute the query. But couldn’t we just have created simple query methods to achieve that? Correct, but remember our second initial requirement. We wanted to be able to freely combine atomic Specifications to create new ones one the fly. To do so we have a helper class Specifications that provides and(…) and or(…) methods to concatenate atomic Specifications. There’s also a where(…) that provides some syntactic sugar to make the expression more readable. The use case sample I came up with in the beginning looks like this:
customerRepository.findAll(where(customerHasBirthday()).and(isLongTermCustomer()));COPY
This reads fluently, improving readability as well as providing additional flexibility as compared to the use of the JPA Criteria API alone. The only caveat here is that coming up with the Specification implementation requires quite some coding effort.
0

评论区