在 Spring Data JPA 查詢中使用 Enum
1. 概述
當使用 Spring Data JPA 建立持久層時,我們經常使用具有枚舉欄位的實體。這些枚舉欄位表示一組固定的常數,例如訂單的狀態、使用者的角色或發布系統中文章的階段。
基於枚舉欄位查詢實體是一種常見需求,Spring Data JPA 提供了多種方法來實現此目的。
在本教程中,我們將探索如何使用標準 JPA 方法和本機查詢來查詢實體類別中聲明的枚舉欄位。
2. 應用程式設定
2.1.資料模型
首先,讓我們定義資料模型,包括枚舉欄位。我們範例中的中心實體是Article類,它聲明了一個枚舉字段ArticleStage來表示一篇文章可能處於的不同階段:
public enum ArticleStage {
TODO, IN_PROGRESS, PUBLISHED;
}
ArticleStage枚舉包含三個可能的階段,代表文章從最初創建到最終發布狀態的生命週期。
接下來,讓我們使用ArticleStage枚舉欄位建立Article實體類別:
@Entity
@Table(name = "articles")
public class Article {
@Id
private UUID id;
private String title;
private String author;
@Enumerated(EnumType.STRING)
private ArticleStage stage;
// standard constructors, getters and setters
}
我們將Article實體類別映射到articles資料庫表。此外,我們使用@Enumerated註解來指定stage欄位應作為字串保存在資料庫中。
2.2.儲存層
定義資料模型後,我們現在可以建立一個儲存庫接口,該介面擴展JpaRepository以與我們的資料庫互動:
@Repository
public interface ArticleRepository extends JpaRepository<Article, UUID> {
}
在接下來的部分中,我們將向此介面新增查詢方法,以探索透過枚舉欄位查詢 Article 實體的不同方法。
3. 標準JPA查詢方法
Spring Data JPA 允許我們使用方法名稱在儲存庫介面中定義派生查詢方法。這種方法非常適合簡單的查詢。
讓我們看看如何使用它來查詢實體類別中的枚舉欄位。
3.1.透過單一枚舉值查詢
我們可以透過在ArticleRepository介面中定義一個方法來透過單一ArticleStage枚舉值來尋找文章:
List<Article> findByStage(ArticleStage stage);
Spring Data JPA 將根據方法名稱產生適當的 SQL 查詢。
我們還可以將stage參數與其他欄位結合以建立更具體的查詢。例如,我們可以聲明一個方法來透過title和stage來尋找文章:
Article findByTitleAndStage(String title, ArticleStage stage);
我們將使用 Instancio 產生測試Article資料並測試這些查詢:
Article article = Instancio.create(Article.class);
articleRepository.save(article);
List<Article> retrievedArticles = articleRepository.findByStage(article.getStage());
assertThat(retrievedArticles).element(0).usingRecursiveComparison().isEqualTo(article);
Article article = Instancio.create(Article.class);
articleRepository.save(article);
Article retrievedArticle = articleRepository.findByTitleAndStage(article.getTitle(), article.getStage());
assertThat(retrievedArticle).usingRecursiveComparison().isEqualTo(article);
3.2.透過多個枚舉值查詢
我們也可以透過多個ArticleStage枚舉值來尋找文章:
List<Article> findByStageIn(List<ArticleStage> stages);
Spring Data JPA 將產生一個 SQL 查詢,該查詢使用IN子句來尋找其階段與任何提供的值相符的文章。
為了驗證我們聲明的方法是否按預期工作,讓我們測試一下:
List<Article> articles = Instancio.of(Article.class).stream().limit(100).toList();
articleRepository.saveAll(articles);
List<ArticleStage> stagesToQuery = List.of(ArticleStage.TODO, ArticleStage.IN_PROGRESS);
List<Article> retrievedArticles = articleRepository.findByStageIn(stagesToQuery);
assertThat(retrievedArticles)
.isNotEmpty()
.extracting(Article::getStage)
.doesNotContain(ArticleStage.PUBLISHED)
.hasSameElementsAs(stagesToQuery);
4. 原生查詢
除了我們在上一節中探討的標準 JPA 方法之外,Spring Data JPA 也支援本機 SQL 查詢。本機查詢對於執行複雜的 SQL 查詢非常有用,並允許我們呼叫特定於資料庫的函數。
此外,我們可以使用 SpEL(Spring 表達式語言)和@Query註解來建構基於方法參數的動態查詢。
讓我們看看如何使用 SpEL 的本機查詢來透過ArticleStage枚舉值查詢實體類別Article 。
4.1.透過單一枚舉值查詢
要使用本機查詢透過單一枚舉值查詢文章記錄,我們可以在ArticleRepository介面中定義一個方法,並使用@Query註解對其進行註解:
@Query(nativeQuery = true, value = "SELECT * FROM articles WHERE stage = :#{#stage?.name()}")
List<Article> getByStage(@Param("stage") ArticleStage stage);
我們將nativeQuery屬性設為true以指示我們使用本機 SQL 查詢而不是預設的 JPQL 定義。
我們在查詢中使用 SpEL 表達式:#{#stage?.name()}來引用傳遞給方法參數的枚舉值。這?表達式中的運算子用於優雅地處理null輸入。
讓我們驗證我們的本機查詢方法是否如預期般運作:
Article article = Instancio.create(Article.class);
articleRepository.save(article);
List<Article> retrievedArticles = articleRepository.getByStage(article.getStage());
assertThat(retrievedArticles).element(0).usingRecursiveComparison().isEqualTo(article);
4.2.透過多個枚舉值查詢
要使用本機查詢透過多個枚舉值查詢文章記錄,我們可以在我們的 ArticleRepository介面:
@Query(nativeQuery = true, value = "SELECT * FROM articles WHERE stage IN (:#{#stages.![name()]})")
List<Article> getByStageIn(@Param("stages") List<ArticleStage> stages);
為了實作此場景,我們在 SQL 查詢中使用IN子句來取得stage與任何提供的值相符的文章。
SpEL 表達式#stages.![name()]將枚舉值清單轉換為表示其名稱的字串清單。
讓我們看看這個方法的行為:
List<Article> articles = Instancio.of(Article.class).stream().limit(100).toList();
articleRepository.saveAll(articles);
List<ArticleStage> stagesToQuery = List.of(ArticleStage.TODO, ArticleStage.IN_PROGRESS);
List<Article> retrievedArticles = articleRepository.findByStageIn(stagesToQuery);
assertThat(retrievedArticles)
.isNotEmpty()
.extracting(Article::getStage)
.doesNotContain(ArticleStage.PUBLISHED)
.hasSameElementsAs(stagesToQuery);
5. 結論
在本文中,我們探討如何使用 Spring Data JPA 查詢實體類別中的枚舉欄位。我們研究了標準 JPA 方法和使用 SpEL 的本機查詢來實現此目的。
我們已經學習如何使用單一和多個枚舉值來查詢實體。標準 JPA 方法提供了一種乾淨、直接的方式來查詢枚舉字段,而本機查詢提供了更多的控制和靈活性來執行複雜的 SQL 查詢。
與往常一樣,本文中使用的所有程式碼範例都可以在 GitHub 上找到。