NonUniqueResultException:JPARepository春季启动

时间:2018-07-03 09:12:44

标签: hibernate spring-boot spring-data-jpa hql

我正在将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

代码或查询或返回类型有什么问题吗?查询似乎可以正常工作。

2 个答案:

答案 0 :(得分:2)

根据Spring Data文档,Map不属于Supported Query Return Types的一部分。
而且,即使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));  

答案 1 :(得分:0)

您的查询结果无法映射到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[2];
    mappedResult.put(ld, count);
}