Spring Data JPA官方文檔:https://docs.spring.io/spring-data/jpa/docs/current/reference/html/html
可參考的開源項目java
-----------------------------------------------------------這是一條性感的分割線-------------------------------------------------------------------------git
在我上一篇文章已經對Spring Data JPA作過了大體上的介紹和分析,Further study of Spring Data JPA - 深刻學習篇(一)咱們從這篇文章接着對其進行深究,Spring Data JPA爲咱們提供了JpaSpecificationExecutor接口,只要簡單實現toPredicate方法就能夠實現複雜的查詢,首先咱們先看看對JpaSpecificationExecutor接口進行分析:github
public interface JpaSpecificationExecutor<T> { Optional<T> findOne(@Nullable Specification<T> spec); List<T> findAll(@Nullable Specification<T> spec); Page<T> findAll(@Nullable Specification<T> spec, Pageable pageable); List<T> findAll(@Nullable Specification<T> spec, Sort sort); long count(@Nullable Specification<T> spec); }
Spring Data JPA經過建立方法名作查詢,只能作簡單的查詢,那若是咱們須要作一些複雜查詢呢?多見見分頁和排序怎麼辦,這裏Spring Data JPA爲咱們提供了其中JpaSpecificationExecutor接口,該接口提供了對JPA Criteria查詢(動態查詢)的支持,只要簡單實現toPredicate方法就能夠實現複雜的查詢,該接口包含了經常使用的單個對象、查詢數據集合、查詢分頁數據集合、查詢帶排序參數的數據集合、查詢數據大小、這些都是產業的數據結果集,所以不須要作其餘定義便可直接使用,另外,Specification是咱們須要傳的參數,它也是一個接口:spring
public interface Specification<T> extends Serializable { long serialVersionUID = 1L; static <T> Specification<T> not(Specification<T> spec) { return Specifications.negated(spec); } static <T> Specification<T> where(Specification<T> spec) { return Specifications.where(spec); } default Specification<T> and(Specification<T> other) { return Specifications.composed(this, other, CompositionType.AND); } default Specification<T> or(Specification<T> other) { return Specifications.composed(this, other, CompositionType.OR); } //提供了toPredicate方法,咱們只要按照JPA 2.0 criteria api寫好查詢條件就能夠了 @Nullable Predicate toPredicate(Root<T> var1, CriteriaQuery<?> var2, CriteriaBuilder var3); }
@Repository public interface MyCustomerRepository extends CrudRepository<Customer,Long>, JpaSpecificationExecutor { }
@Service public class MyCustomerService { @Autowired MyCustomerRepository repository; /** * 獲取分頁列表數據 * @param customer 實體對象 * @return 返回分頁數據 */ public Page<Customer> getPageList(Customer customer){ PageRequest pageable = PageRequest.of(1,10,Sort.Direction.ASC); //使用Specification複雜查詢 return repository.findAll(new Specification() { @Override public Predicate toPredicate(Root root, CriteriaQuery cq, CriteriaBuilder cb) { List<Predicate> predicates = new ArrayList<>(); if (customer.getId()!=null){ predicates.add(cb.equal(root.get("id").as(Long.class),customer.getId())); } if (customer.getFirstName()!=null){ predicates.add(cb.like(root.get("firstName").as(String.class),customer.getFirstName())); } if (customer.getFirstName()!=null){ predicates.add(cb.like(root.get("lastName").as(String.class),customer.getLastName())); } // todo 具體的業務邏輯實現 // return (Predicate) repository.findAll(); } },pageable); } }
/** * 多條件查詢方式一 * 需求:根據客戶firstName和lastName以及年齡條件查詢 */ public void test1(){ Specification specification = new Specification() { @Override public Predicate toPredicate(Root root, CriteriaQuery cq, CriteriaBuilder cb) { Predicate predicateCustomerFirstName = cb.equal(root.get("firstNane"),"Bauer"); Predicate predicateCustomerLastName = cb.equal(root.get("lastName"),"Mdessler"); Predicate predicateCustomerAge = cb.ge(root.get("age"),20); List<Predicate> list = new ArrayList<>(); list.add(predicateCustomerFirstName); list.add(predicateCustomerLastName); list.add(predicateCustomerAge); Predicate[] arr = new Predicate[list.size()]; return cb.and(list.toArray(arr)); } }; List<Customer> customerList = this.myCustomerRepository.findAll(specification); for (Customer customer:customerList){ System.out.println(customer); } } /** * 多條件查詢方式二 * 需求:根據客戶firstName或年齡或id爲10的查詢 */ public void test2(){ Specification specification = new Specification() { @Override public Predicate toPredicate(Root root, CriteriaQuery cq, CriteriaBuilder cb) { return cb.or(cb.and(cb.equal(root.get("firstName"),"Tony"), cb.equal(root.get("id"),5), cb.ge(root.get("age"),25))); } }; List<Customer> customerList = this.myCustomerRepository.findAll(specification); for(Customer customer:customerList){ System.out.println(customer); } }
@Autowired MyCustomerRepository repository;
/** * 單一查詢 */ @RequestMapping("/spec1") public void specificationQuery1(){ Specification<Customer> spec = SpecificationFactory.containsLike("lastName","Dessler"); Pageable pageable = PageRequest.of(0,5, Sort.Direction.ASC,"id"); Page<Customer> page = repository.findAll(spec, pageable); System.out.println(page); System.out.println(page.getTotalElements()); System.out.println(page.getTotalPages()); for (Customer customer:page.getContent()){ System.out.println(customer.toString()); } }
/** * 複合條件查詢 */ @RequestMapping(value = "/spec2") public void specificationQuery2(){ Specification<Customer> spec = Specification .where(SpecificationFactory.containsLike("firstName","To")) .or(SpecificationFactory.containsLike("lastName","Dess")); Pageable pageable = PageRequest.of(0,5,Sort.Direction.DESC,"id"); Page<Customer> page = repository.findAll(spec, pageable); System.out.println(page); System.out.println(page.getTotalPages()); System.out.println(page.getTotalElements()); for (Customer customer:page.getContent()){ System.out.println(customer.toString()); } }
至此,使用Specification經過Criteria API進行查詢,得到查詢的結果集。相較於@Query方式的查詢定義,更人性化和方便操做,後面在對Criteria API的使用進一步深刻,進一步瞭解他的底層原理和實現。api