Spring Data JPA最爲優秀的特性就是能夠經過自定義方法名稱生成查詢來輕鬆建立查詢SQL。Spring Data JPA提供了一個Repository編程模型,最簡單的方式就是經過擴展JpaRepository,咱們得到了一堆通用的CRUD方法,例如save,findAll,delete等。而且使用這些關鍵字能夠構建不少的數據庫單表查詢接口:java
public interface CustomerRepository extends JpaRepository<Customer, Long> { Customer findByEmailAddress(String emailAddress); List<Customer> findByLastname(String lastname, Sort sort); Page<Customer> findByFirstname(String firstname, Pageable pageable); }
以上全部的查詢都不用咱們手寫SQL,查詢生成器自動幫咱們工做,對於開發人員來講只須要記住一些關鍵字,如:findBy、delete等等。可是,有時咱們須要建立複雜一點的查詢,就沒法利用查詢生成器。可使用本節介紹的Specification來完成。spring
筆者仍是更願意手寫SQL來完成複雜查詢,可是有的時候偶爾使用一下Specification來完成任務,也仍是深得我心。不排斥、不盲從。沒有最好的方法,只有最合適的方法!數據庫
是的,除了specification,咱們還可使用Criteria API構建複雜的查詢,可是沒有specification好用。咱們來看一下需求:在客戶生日當天,咱們但願向全部長期客戶(2年以上)發送優惠券。咱們如何該檢索Customer?編程
咱們有兩個謂詞查詢條件:api
下面是使用JPA 2.0 Criteria API的實現方式:springboot
LocalDate today = new LocalDate(); CriteriaBuilder builder = em.getCriteriaBuilder(); CriteriaQuery<Customer> query = builder.createQuery(Customer.class); Root<Customer> root = query.from(Customer.class); Predicate hasBirthday = builder.equal(root.get(Customer_.birthday), today); Predicate isLongTermCustomer = builder.lessThan(root.get(Customer_.createdAt), today.minusYears(2); query.where(builder.and(hasBirthday, isLongTermCustomer)); em.createQuery(query.select(root)).getResultList();
LocalDate
用於比較客戶的生日和今天的日期。em是javax.persistence.EntityManager此代碼的主要問題在於,謂詞查詢條件不易於重用,您須要先設置 CriteriaBuilder, CriteriaQuery
,和Root
。另外,代碼的可讀性也不好。less
爲了可以定義可重用謂詞條件,咱們引入了Specification接口。學習
public interface Specification<T> { Predicate toPredicate(Root<T> root, CriteriaQuery query, CriteriaBuilder cb); }
結合Java 8的lambda表達式使用Specification接口時,代碼變得很是簡單ui
public CustomerSpecifications { //查詢條件:生日爲今天 public static Specification<Customer> customerHasBirthday() { return (root, query, cb) ->{ return cb.equal(root.get(Customer_.birthday), today); }; } //查詢條件:客戶建立日期在兩年之前 public static Specification<Customer> isLongTermCustomer() { return (root, query, cb) ->{ return cb.lessThan(root.get(Customer_.createdAt), new LocalDate.minusYears(2)); }; } }
如今能夠經過CustomerRepository執行如下操做:code
customerRepository.findAll(hasBirthday()); customerRepository.findAll(isLongTermCustomer());
咱們建立了能夠單獨執行的可重用謂詞查詢條件,咱們能夠結合使用這些單獨的謂詞來知足咱們的業務需求。咱們可使用 and(…)
和 or(…)
鏈接specification。
customerRepository.findAll(where(customerHasBirthday()).and(isLongTermCustomer()));
與使用JPA Criteria API相比,它讀起來很流利,提升了可讀性並提供了更多的靈活性。