我正在将SpringBoot
和JPA
和QueryDSL
一起使用。我已经写了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
代码或查询或返回类型有什么问题吗?查询似乎可以正常工作。
答案 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);
}