概述

Spring Data JPA 提供了一个简单且一致的接口,用于访问存储在各种关系型数据库中的数据,使得开发者能够编写不依赖于特定数据库的代码。它减少了样板代码的需求,让开发者可以专注于构建应用程序的业务逻辑。

然而,我们仍需确保返回正确的类型,否则会抛出异常。在这篇教程中,我们将重点关注NonUniqueResultException。我们将学习其原因,并在遇到时如何修复代码。

2. NonUniqueResultException

当使用Spring Data JPA的查询方法(如findById()findOne()或自定义不返回集合的方法)执行查询时,如果预期结果应为单个结果但找到多个结果,Spring Data JPA框架会抛出一个NonUniqueResultException运行时异常。这可能是由于查询错误或数据库中的数据不一致导致的。

3. 示例

让我们使用之前文章中的实体——使用Spring Data JPA按日期和时间查询实体

@Entity
public class Article {

    @Id
    @GeneratedValue
    private Integer id;

    @Temporal(TemporalType.DATE)
    private Date publicationDate;

    @Temporal(TemporalType.TIME)
    private Date publicationTime;

    @Temporal(TemporalType.TIMESTAMP)
    private Date creationDateTime;
    }
}

现在,创建ArticleRepository并添加两个方法:

public interface ArticleRepository extends JpaRepository<Article, Integer> {

    List<Article> findAllByPublicationTimeBetween(Date publicationTimeStart, Date publicationTimeEnd);

    Article findByPublicationTimeBetween(Date publicationTimeStart, Date publicationTimeEnd);
}

两个方法之间的唯一区别是findAllByPublicationTimeBetween()返回一个List<Article>,而findByPublicationTimeBetween()返回单个Article

当我们执行第一个方法findAllByPublicationTimeBetween()时,总是会得到一个集合。根据数据库中的数据,可能会得到空的List,也可能包含一个或多个Article实例。

第二个方法findByPublicationTimeBetween()理论上也有效,假设数据库中对于给定查询恰好有零个或一个匹配的Article。如果没有找到匹配的单个条目,方法将返回null。相反,如果有匹配的Article,则返回单个Article

但如果findByPublicationTimeBetween()查询到多个匹配的Article,方法将抛出NonUniqueResultException,然后被包装在一个IncorrectResultSizeDataAccessException中。

当这类异常在运行时随机出现时,表明数据库设计或方法实现存在问题。在下一节中,我们将学习如何避免这种错误。

4. 避免NonUniqueResultException的提示

为了避免NonUniqueResultException,关键在于精心设计数据库查询并正确使用Spring Data JPA的查询方法。设计查询时,确保其始终返回期望数量的结果至关重要。我们可以通过仔细指定查询条件来实现,例如使用唯一键或其他标识信息。

在设计查询方法时,遵循以下基本规则以避免NonUniqueResultException

  • 如果可能返回多个值,应使用 ListSet 作为返回类型。
  • 只使用单个返回值,如果我们能通过数据库设计确保只有一个返回值。通常情况下,当我们查找唯一的键,如IdUUID,或者在某些数据库设计中,电子邮件或电话号码也是保证唯一的。
  • 另一种确保只有一个返回值的方法是**限制返回结果的数量为单个元素**。这在想要获取最新Article时尤其有用。

5. 总结

在使用Spring Data JPA时,理解并避免NonUniqueResultException非常重要。当预期查询结果为单个结果但找到多个时,就会出现这种情况。我们可以通过确保JpaRepository方法返回正确数量的元素并相应地指定正确的返回类型来防止这个问题。

通过理解和适当地避免NonUniqueResultException,我们可以确保应用程序能够一致且可靠地从数据库访问数据。

如往常一样,这些示例也可在GitHub上找到。