NonUniqueResultException: загрузка JPARepository Spring - PullRequest
0 голосов
/ 03 июля 2018

Я использую 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

Что-то не так с кодом, типом запроса или возврата? Похоже, что запрос работает нормально.

Ответы [ 2 ]

0 голосов
/ 03 июля 2018

Согласно документации Spring Data, Map не входит в Поддерживаемые типы возврата запросов .
И даже JPA (даже версия 2) не поддерживает Map в качестве возвращаемого типа в выполненных запросах.

Итак, у вас есть два способа решения вашей проблемы:

1) сохранить Map в качестве типа возврата. В этом случае не используйте функцию Spring Data, которая освобождает вас от написания кода котельной пластины.
Вместо этого: создайте запрос из EntityManager, выполните его и примените постобработку, чтобы отобразить результат в карту.
Если 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);

Обратите внимание, что преобразование List в Map в Java 8 очень просто:

List<LoanOfferStats> loanOfferStats = loanOfferRepository.getLastMonthLoans(...);
Map<LocalDate, Long> map = 
        loanOfferStats.stream()
                      .collect(Collectors.toMap(LoanOfferStats::getStartDate, LoanOfferStats::getCount));  
0 голосов
/ 03 июля 2018

Ваш результат запроса не может быть сопоставлен с 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);
}
...