NonUniqueResultException:JPARepository Spring 启动

NonUniqueResultException: JPARepository Spring boot

我将 SpringBootJPAQueryDSL 一起使用。我写了一个 HQL 来从 table 中获取一些自定义记录,但它抛出了 Exception。下面我提到存储库的代码:

@Repository
public interface LoanOfferRepository extends JpaRepository<LoanOffer, Long>, QuerydslPredicateExecutor<LoanOffer> {

    @Query("select lo.startDate,count(*) from LoanOffer lo where lo.loan.fsp= :fsp and lo.startDate between :fromDate and :toDate Group by lo.startDate")
    public Map<LocalDate,Integer> getLastMonthLoans(@Param("fsp")Fsp fsp,@Param("fromDate")LocalDate fromDate,@Param("toDate")LocalDate toDate);
}

每当我调用此方法时 getLastMonthLoans() 我都会收到以下异常:

Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.dao.IncorrectResultSizeDataAccessException: query did not return a unique result: 9; nested exception is javax.persistence.NonUniqueResultException: query did not return a unique result: 9] with root cause

javax.persistence.NonUniqueResultException: query did not return a unique result: 9

代码或查询或Return类型有什么问题吗?不过,查询似乎工作正常。

您的查询结果无法映射到Map<LocalDate,Integer>

您可以尝试 return List<Object[]> 而不是 Map

@Query("select lo.startDate,count(*) from LoanOffer lo where lo.loan.fsp= :fsp and lo.startDate between :fromDate and :toDate Group by lo.startDate")
public List<Object[]> getLastMonthLoans(@Param("fsp")Fsp fsp,@Param("fromDate")LocalDate fromDate,@Param("toDate")LocalDate toDate);

然后将List<Object[]>解析为你需要的Map

因此:

Map<LocalDate, Integer> mappedResult = new HashMap<>();
List<Object[]> queryResult = loanOfferRepository.getLastMonthLoans(fsp, fromDate, toDate);
for (Object[] obj : queryResult ) {
    LocalDate ld = (LocalDate) obj[0];
    Integer count = (Integer) obj[1];
    mappedResult.put(ld, count);
}

根据 Spring 数据文档,Map 不属于 Supported Query Return Types
甚至 JPA(即使是 2 版本)也不支持 Map 作为 return 类型执行查询。

所以您有两种方法可以解决您的问题:

1) 将 Map 保留为 return 类型。在这种情况下,不要使用 Spring 数据功能,它可以让您免于编写样板代码。
相反:从 EntityManager 创建查询,执行它并应用 post 处理以将结果映射到 Map。
如果 Map 具有合理的大小并且您确实需要从您的存储库中检索 Map,那么应该优先使用这种方式。

2) 不要 return a Map 作为 return 类型。

在这两种情况下,您都必须选择所执行查询的 return 类型。您有两种选择:

1) List<Object[]> 为 return 类型,但它不一定有意义,也不安全。

2) 表示行结构的自定义 class

public class LoanOfferStats{
  private LocalDate startDate;
  private Long count;

  public LoanOfferStats(LocalDate startDate, Long count) {
    this.startDate = startDate;
    this.count  = count;
  }

  public LocalDate getStartDate(){
     return startDate;
  }

  public Long getCount(){
     return count;
  }

}

并注释您的方法,例如:

 @Query("select new fullpackage.LoanOfferStats(lo.startDate,count(*))
 from LoanOffer lo where lo.loan.fsp= :fsp and lo.startDate between
 :fromDate and :toDate Group by lo.startDate")
     public List<LoanOfferStats> getLastMonthLoans(@Param("fsp")Fsp fsp,@Param("fromDate")LocalDate fromDate,@Param("toDate")LocalDate
 toDate);

请注意,将 List 转换为 Map 在 Java 8 中非常直接:

List<LoanOfferStats> loanOfferStats = loanOfferRepository.getLastMonthLoans(...);
Map<LocalDate, Long> map = 
        loanOfferStats.stream()
                      .collect(Collectors.toMap(LoanOfferStats::getStartDate, LoanOfferStats::getCount));