使用 CriteriaQuery 對 JPA 中的查詢進行計數
一、簡介
Java Persistence API (JPA) 是一種廣泛使用的規範,用於存取、保存和管理 Java 物件和關聯式資料庫之間的資料。 JPA 應用程式中的常見任務是計算滿足特定條件的實體的數量。使用 JPA 提供的CriteriaQuery
API 可以有效地完成此任務。
Criteria Query 的核心元件是CriteriaBuilder
和CriteriaQuery
介面。 CriteriaBuilder
是用來建立各種查詢元素(例如謂詞、表達式和條件查詢)的工廠。另一方面, CriteriaQuery
表示封裝了選擇、篩選和排序條件的查詢物件。
在本文中,我們將研究 JPA 中的計數查詢,探索如何利用 Criteria Query API 輕鬆有效率地執行計數操作。我們將首先快速概述CriteriaQuery
以及如何使用它們來產生計數查詢。接下來,我們將採用一個圖書館管理系統的簡單範例,在該系統中我們可以利用CriteriaQuery
API 來產生各種場景下的圖書計數。
2. 依賴關係和範例設置
首先,確保我們擁有所需的 Maven 依賴項,包括[spring-data-jpa](https://mvnrepository.com/artifact/org.springframework.data/spring-data-jpa)
、 [spring-boot-starter-test ,](https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-test)
和h2作為記憶體資料庫:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
現在我們的依賴關係已經設定好了,讓我們介紹一個圖書館管理系統的簡單範例。它將允許我們執行各種查詢,例如對所有書籍進行計數,以各種組合計算特定作者、書名和年份的書籍。讓我們引入一個帶有title
、 author, category,
和year
欄位的Book
實體:
@Entity
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
private String Category;
private String author;
private int year;
// Constructors, getters, and setters
}
接下來,讓我們編寫一個儲存庫接口,該接口允許我們對Book
實體執行各種操作:
public interface BookRepositoryCustom {
long countAllBooks();
long countBooksByTitle(String title);
long countBooksByAuthor(String author);
long countBooksByCategory(String category);
long countBooksByTitleAndAuthor(String title, String author);
long countBooksByAuthorOrYear(String author, int year);
}
3. 使用CriteriaQuery
計算實體數量
計數查詢通常用於確定滿足特定條件的實體總數。使用CriteriaQuery
,我們可以直接有效地建構計數查詢。讓我們深入了解如何使用 JPA 中的條件查詢執行計數查詢的逐步指南。
3.1.初始化CriteriaBuilder
和CriteriaQuery
要開始建構計數查詢,我們首先需要從EntityManager
取得CriteriaBuilder
的實例。 CriteriaBuilder
作為我們創建查詢元素的入口點:
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
此物件用於建構查詢的不同部分,例如條件查詢、表達式、謂詞和選擇。接下來讓我們建立一個條件查詢:
CriteriaQuery<Long> cq = cb.createQuery(Long.class);
在這裡,我們將查詢的結果類型指定為 Long,表示我們希望查詢傳回一個計數值。
3.2.建立Root
並選擇計數
接下來,讓我們建立一個Root
物件來表示我們要執行計數操作的實體。然後,我們將使用CriteriaBuilder
基於此Root
物件建構一個計數表達式:
Root<Book> bookRoot = cq.from(Book.class);
cq.select(cb.count(bookRoot));
在這裡,首先,我們定義查詢的根,指定查詢是基於Book
實體。接下來,我們使用cb.count() provided by CriteriaBuilder.
count
方法計算查詢結果中的行數。它接受一個表達式(在本例中為bookRoot
)作為參數,並傳回一個表達式,該表達式表示與查詢中定義的條件相符的行數
最後, 然後, cq.select()
將查詢結果設定為此計數表達式。本質上,它告訴查詢最終結果應該是符合指定條件的Book
實體的數量(如果有的話,我們稍後會看到)。
3.3.執行查詢
一旦 CriteriaQuery 建置完成,我們就可以使用EntityManager:
Long count = entityManager.createQuery(cq).getSingleResult();
在這裡,我們使用entityManager.createQuery(cq)
從CriteriaQuery
建立TypedQuery
,並getSingleResult()
將計數值作為單一結果檢索。
4. 處理標準與條件
在現實場景中,計數查詢通常涉及基於某些標準或條件的過濾。 Criteria Query 提供了一個靈活的機制,可以使用謂詞為查詢添加條件。
讓我們更深入地了解如何實現一些利用多個條件產生計數查詢的場景。假設我們想要取得標題中包含某個關鍵字的所有書籍的計數:
long countBooksByTitle(String titleKeyword) {
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Long> cq = cb.createQuery(Long.class);
Root<Book> bookRoot = cq.from(Book.class);
Predicate condition = cb.like(bookRoot.get("title"), "%" + titleKeyword + "%");
cq.where(condition);
cq.select(cb.count(bookRoot));
return entityManager.createQuery(cq).getSingleResult()
}
除了前面的步驟之外,對於條件計數,我們建立一個表示 SQL 查詢的 WHERE 子句的謂詞。
cb.like
方法建立一個條件來檢查書名是否包含 title 關鍵字。 % 是符合任意字元序列的通配符。然後,我們使用cq.where(condition),
這會將此條件套用到查詢。
我們場景中的另一個用例可能是獲取作者所有書籍的計數:
long countBooksByAuthor(String authorName) {
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Long> cq = cb.createQuery(Long.class);
Root<Book> bookRoot = cq.from(Book.class);
Predicate condition = cb.equal(bookRoot.get("author"), authorName);
cq.where(condition);
cq.select(cb.count(bookRoot));
return entityManager.createQuery(cq).getSingleResult();
}
這裡,謂詞基於cb.equal()
方法,該方法僅過濾包含確切作者姓名的記錄。
5. 結合多個標準
Criteria Query 允許我們使用 AND、OR 和 NOT 等邏輯運算子組合多個條件。讓我們考慮一些我們想要基於多個條件計算圖書數量的情況。 假設我們想要取得特定作者、書名和出版年份的所有書籍數量:
long countBooksByAuthorOrYear(int publishYear, String authorName) {
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Long> cq = cb.createQuery(Long.class);
Root<Book> bookRoot = cq.from(Book.class);
Predicate authorCondition = cb.equal(bookRoot.get("author"), authorName);
Predicate yearCondition = cb.greaterThanOrEqualTo(bookRoot.get("publishYear"), 1800);
cq.where(cb.or(authorCondition, yearCondition));
cq.select(cb.count(bookRoot));
return entityManager.createQuery(cq).getSingleResult();
}
在這裡,我們創建兩個謂詞來表示書籍的作者和出版年份的條件。然後,我們使用cb.and()
組合這些謂詞以形成複合條件。
類似地,我們還可以有一個場景,我們想要獲得具有特定標題或具有作者和年份組合的書籍的數量:
long countBooksByTitleOrYearAndAuthor(String authorName, int publishYear, String titleKeyword) {
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Long> cq = cb.createQuery(Long.class);
Root<Book> bookRoot = cq.from(Book.class);
Predicate authorCondition = cb.equal(bookRoot.get("author"), authorName);
Predicate yearCondition = cb.equal(bookRoot.get("publishYear"), publishYear);
Predicate titleCondition = cb.like(bookRoot.get("title"), "%" + titleKeyword + "%");
Predicate authorAndYear = cb.and(authorCondition, yearCondition);
cq.where(cb.or(authorAndYear, titleCondition));
cq.select(cb.count(bookRoot));
return entityManager.createQuery(cq).getSingleResult();
}
我們再次創建了三個謂詞,但現在我們希望使用cb.or(authorAndYear, titleCondition)
在authorAndYearCondition
謂詞和titleCondition predicate
之間有一個 or 。
6. 整合測試
現在讓我們提供一個執行整合測試的模式,確保我們的計數查詢工作。我們將使用Spring提供的the @DataJPATest
註解在我們的測試中註入必要的儲存庫層,使用記憶體資料庫中的H2作為底層持久性儲存。我們將TestEntityManager in our test class and use it
來插入資料。讓我們舉一個例子,取得某個作者的所有書籍的數量:
@Test
void givenBookDataAdded_whenCountBooksByAuthor_thenReturnsCount() {
entityManager.persist(new Book("Java Book 1", "Author 1", 1967, "Non Fiction"));
entityManager.persist(new Book("Java Book 2", "Author 1", 1999, "Non Fiction"));
entityManager.persist(new Book("Spring Book", "Author 2", 2007, "Non Fiction"));
long count = bookRepository.countBooksByAuthor("Author 1");
assertEquals(2, count);
}
與上面類似,我們可以為儲存庫中提供的所有不同計數場景編寫測試。
七、結論
在本文中,我們示範如何使用 JPA Criteria API 在 Spring Boot 應用程式中執行條件計數。我們建立了一個基本的圖書館管理系統,並實作了自訂儲存庫方法來根據特定條件對圖書進行計數。
與往常一樣,本文的完整實作可以在 GitHub 上找到。