正確使用Spring Data JPA規范


在優銳課的學習分享中探討了關於,Spring Data JPA的創建主要是為了通過按方法名稱生成查詢來輕松創建查詢。 但是,有時我們需要創建復雜的查詢,而無法利用查詢生成器。碼了很多知識筆記分享給大家。

Spring Data JPA提供了一個存儲庫編程模型,該模型以每個受管域對象的接口開頭。 定義這些接口有兩個目的:首先,通過擴展JpaRepository,我們獲得了一堆通用的CRUD方法,例如save,findAll,delete等。 其次,這將允許Spring Data JPA存儲庫基礎結構掃描該接口的類路徑並為其創建Spring Bean。 典型的存儲庫界面如下所示:

 1 public interface CustomerRepository extends JpaRepository<Customer, Long> {
 2 
 3   Customer findByEmailAddress(String emailAddress);
 4 
 5   List<Customer> findByLastname(String lastname, Sort sort);
 6 
 7   Page<Customer> findByFirstname(String firstname, Pageable pageable);
 8 
 9 }
10 
11  

 

要創建復雜的查詢,為什么要指定規格?

是的,可以使用Criteria API構建復雜的查詢。 要了解為什么要使用規范,我們考慮一個簡單的業務需求。 我們將使用Criteria API以及隨后的規范來實現此要求。

這是用例:在客戶生日那天,我們希望向所有長期客戶發送優惠券。 我們如何檢索一個匹配的?

我們有兩個謂詞:

 1 LocalDate today = new LocalDate();
 2 
 3 CriteriaBuilder builder = em.getCriteriaBuilder();
 4 
 5 CriteriaQuery<Customer> query = builder.createQuery(Customer.class);
 6 
 7 Root<Customer> root = query.from(Customer.class);
 8 
 9 Predicate hasBirthday = builder.equal(root.get(Customer_.birthday), today);
10 
11 Predicate isLongTermCustomer = builder.lessThan(root.get(Customer_.createdAt), today.minusYears(2);
12 
13 query.where(builder.and(hasBirthday, isLongTermCustomer));
14 
15 em.createQuery(query.select(root)).getResultList();
16 
17  

 

在上面的代碼中,

  • ·第一行創建了LocalDate以比較客戶的生日和今天的日期。
  • ·以下三行包含用於設置必要的JPA基礎結構實例的樣板代碼。
  • ·然后,在接下來的兩行中,我們將構建謂詞
  • ·在最后兩行中,一個用於連接兩個謂詞,最后一個用於執行查詢。

此代碼的主要問題在於謂詞不易於外部化和重用,因為您需要先設置CriteriaBuilder,CriteriaQuery和Root。 另外,由於難以快速推斷出代碼的意圖,因此代碼的可讀性很差。

規格

為了能夠定義可重用謂詞,我們引入了規范接口,該接口源自Eric Evans的《域驅動設計》一書中引入的概念。 它將規范定義為實體的謂詞,這正是規范接口所代表的含義。 實際上,這僅包含一個方法:

1 public interface Specification<T> {
2 
3   Predicate toPredicate(Root<T> root, CriteriaQuery query, CriteriaBuilder cb);
4 
5 }
6 
7  

 

使用Java 8時,代碼變得非常清晰易懂。

 1 public CustomerSpecifications {
 2 
 3   public static Specification<Customer> customerHasBirthday() {
 4 
 5 return (root, query, cb) ->{
 6 
 7 return cb.equal(root.get(Customer_.birthday), today);
 8 
 9 };
10 
11  }
12 
13  public static Specification<Customer> isLongTermCustomer() {
14 
15 return (root, query, cb) ->{
16 
17         return cb.lessThan(root.get(Customer_.createdAt), new LocalDate.minusYears(2));
18 
19 };
20 
21 }
22 
23 }
24 
25  

 

客戶端現在可以執行以下操作:

1 customerRepository.findAll(hasBirthday());
2 
3 customerRepository.findAll(isLongTermCustomer());
4 
5  

 

在這里,基本實現將為您准備CriteriaQuery,Root和CriteriaBuilder,應用由給定規范創建的謂詞並執行查詢。

我們只是創建了可以單獨執行的可重用謂詞。 我們可以結合使用這些單獨的謂詞來滿足我們的業務需求。 我們有一個幫助程序類規范,它提供了(和)和(或)方法來連接原子規范。

1 customerRepository.findAll(where(customerHasBirthday()).and(isLongTermCustomer()));

 

 

與僅使用JPA Criteria API相比,它讀起來很流利,提高了可讀性並提供了更多的靈活性。 這里唯一需要說明的是,提出規范實現需要相當多的編碼工作。

以下是規范的一些優點:

  1. 所有“基本”查詢都已實現,即findById,保存和刪除
  2. 分頁功能開箱即用。 您可以簡單地將一個可分頁對象從Controller傳遞到Service到您的存儲庫,並且可以正常工作(甚至可以排序)!
  3. 使用Spring的Specification API比普通的JPA簡單一些,因為您只需創建謂詞,而不必弄亂EntityManager和PersistenceContext。

如果您有任何要添加或共享的內容,請在下面的評論部分中留言。

祝大家學習愉快!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM