NonUniqueResultException:JPARepository Spring boot 非唯一结果异常:JPARepository Spring Boot

3

我正在使用SpringBootJPAQueryDSL。我编写了一个HQL来从表中获取一些自定义记录,但是它抛出了一个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

代码、查询或返回类型是否有任何问题?虽然查询似乎正常工作。


你应该使用投影。请查看我的答案12,了解如何从repo返回任意对象。 - Cepr0
2个回答

5

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

您可以尝试返回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);
}

2
根据Spring Data文档,Map不属于支持的查询返回类型。即使是JPA(包括2版本),也不支持在执行的查询中使用Map作为返回类型。
因此,你有两种解决方法:
1) 保持Map作为返回类型,这种情况下,不要使用Spring Data特性,以减少编写样板代码的工作。
而是:从EntityManager创建查询,执行查询并应用后处理将结果映射到Map中。
如果Map具有合理的大小,并且确实需要从存储库检索Map,则应优先使用这种方式。
2) 不要返回Map作为返回类型。
在这两种情况下,你都必须选择执行查询的返回类型。你大致上有两个选择:
1) 将List<Object[]>作为返回类型,但这不一定有意义,也不够类型安全。
2) 使用表示行结构的自定义类作为返回类型。
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);

请注意,在Java 8中将List转换为Map非常简单:
List<LoanOfferStats> loanOfferStats = loanOfferRepository.getLastMonthLoans(...);
Map<LocalDate, Long> map = 
        loanOfferStats.stream()
                      .collect(Collectors.toMap(LoanOfferStats::getStartDate, LoanOfferStats::getCount));  

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接