在Spring Data JPA查询中使用枚举

2025/03/23

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 Repository层

定义了数据模型后,我们现在可以创建一个扩展JpaRepository的Repository接口来与我们的数据库进行交互:

@Repository
public interface ArticleRepository extends JpaRepository<Article, UUID> {
}

在接下来的部分中,我们将向该接口添加查询方法,以探索通过枚举字段查询Article实体的不同方法。

3. 标准JPA查询方法

Spring Data JPA允许我们使用方法名称在Repository接口中定义派生查询方法,这种方法非常适合简单查询。

让我们研究一下如何使用它来查询实体类中的枚举字段。

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 = 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子句来查找stage与任何提供的值匹配的文章。

为了验证我们声明的方法是否按预期工作,让我们对其进行测试:

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查询很有用,并允许我们调用特定于数据库的函数。

此外,我们可以使用带有@Query注解的SpEL(Spring Expression Language)来根据方法参数构建动态查询。

让我们看看如何使用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()}来引用传递给方法参数的枚举值,表达式中的?运算符用于优雅地处理空输入

让我们验证一下原生查询方法是否按预期工作:

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查询。

Show Disqus Comments

Post Directory

扫码关注公众号:Taketoday
发送 290992
即可立即永久解锁本站全部文章