Rozwiązanie dla zapytań JPQL
Jest to obsługiwane w przypadku zapytań JPQL w ramach specyfikacji JPA .
Krok 1 : Zadeklaruj prostą klasę fasoli
package com.path.to;
public class SurveyAnswerStatistics {
private String answer;
private Long cnt;
public SurveyAnswerStatistics(String answer, Long cnt) {
this.answer = answer;
this.count = cnt;
}
}
Krok 2 : Zwróć instancje bean z metody repozytorium
public interface SurveyRepository extends CrudRepository<Survey, Long> {
@Query("SELECT " +
" new com.path.to.SurveyAnswerStatistics(v.answer, COUNT(v)) " +
"FROM " +
" Survey v " +
"GROUP BY " +
" v.answer")
List<SurveyAnswerStatistics> findSurveyCount();
}
Ważne notatki
- Upewnij się, że podano w pełni kwalifikowaną ścieżkę do klasy bean, w tym nazwę pakietu. Na przykład, jeśli wywoływana jest klasa bean
MyBean
i znajduje się ona w pakiecie com.path.to
, w pełni kwalifikowana ścieżka do komponentu bean będzie com.path.to.MyBean
. Samo podanie MyBean
nie zadziała (chyba że klasa bean znajduje się w domyślnym pakiecie).
- Pamiętaj, aby wywołać konstruktor klasy bean przy użyciu
new
słowa kluczowego. SELECT new com.path.to.MyBean(...)
zadziała, ale SELECT com.path.to.MyBean(...)
nie zadziała.
- Upewnij się, że atrybuty są przekazywane dokładnie w takiej samej kolejności, jak oczekiwano w konstruktorze bean. Próba przekazania atrybutów w innej kolejności doprowadzi do wyjątku.
- Upewnij się, że zapytanie jest prawidłowym zapytaniem JPA, czyli nie jest zapytaniem natywnym.
@Query("SELECT ...")
, lub @Query(value = "SELECT ...")
, lub @Query(value = "SELECT ...", nativeQuery = false)
będzie działać, ale @Query(value = "SELECT ...", nativeQuery = true)
nie będzie działać. Dzieje się tak, ponieważ natywne zapytania są przekazywane bez modyfikacji do dostawcy JPA i są wykonywane względem bazowego RDBMS jako takiego. Ponieważ new
i com.path.to.MyBean
nie są poprawnymi słowami kluczowymi SQL, RDBMS zgłasza wyjątek.
Rozwiązanie dla zapytań natywnych
Jak wspomniano powyżej, new ...
składnia jest mechanizmem obsługiwanym przez JPA i działa ze wszystkimi dostawcami JPA. Jeśli jednak samo zapytanie nie jest zapytaniem JPA, to znaczy jest zapytaniem natywnym, new ...
składnia nie będzie działać, ponieważ zapytanie jest przekazywane bezpośrednio do bazowego systemu RDBMS, który nie rozumie new
słowa kluczowego, ponieważ nie jest częścią standard SQL.
W takich sytuacjach klasy bean należy zastąpić interfejsami Spring Data Projection .
Krok 1 : Zadeklaruj interfejs projekcji
package com.path.to;
public interface SurveyAnswerStatistics {
String getAnswer();
int getCnt();
}
Krok 2 : Zwróć przewidywane właściwości z zapytania
public interface SurveyRepository extends CrudRepository<Survey, Long> {
@Query(nativeQuery = true, value =
"SELECT " +
" v.answer AS answer, COUNT(v) AS cnt " +
"FROM " +
" Survey v " +
"GROUP BY " +
" v.answer")
List<SurveyAnswerStatistics> findSurveyCount();
}
Użyj AS
słowa kluczowego SQL, aby odwzorować pola wynikowe na właściwości rzutowania w celu jednoznacznego odwzorowania.
Caused by: java.lang.IllegalArgumentException: org.hibernate.hql.internal.ast.QuerySyntaxException: Unable to locate class [SurveyAnswerReport] [select new SurveyAnswerReport(v.answer,count(v.id)) from com.furniturepool.domain.Survey v group by v.answer] at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1750) at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1677) at org.hibernate.jpa.spi.AbstractEnti..........
SurveyAnswerReport
w twoim wyniku. Zakładam, że zastąpiłeśSurveyAnswerStatistics
własną klasąSurveyAnswerReport
. Musisz określić w pełni kwalifikowaną nazwę klasy.com.domain.dto.SurveyAnswerReport
.JpaRepository
? Czy brakuje mi jakiejś konfiguracji?To zapytanie SQL zwróci List <Object []>.
Możesz to zrobić w ten sposób:
źródło
Wiem, że to stare pytanie i już udzielono na nie odpowiedzi, ale oto inne podejście:
źródło
Korzystając z interfejsów, możesz uzyskać prostszy kod. Nie ma potrzeby tworzenia i ręcznego wywoływania konstruktorów
Krok 1 : Zadeklaruj interakcję z wymaganymi polami:
Krok 2 : Wybierz kolumny o tej samej nazwie co getter w interfejsie i zwróć intefrace z metody repozytorium:
źródło
zdefiniuj niestandardową klasę pojo, powiedz sureveyQueryAnalytics i zapisz wartość zwróconą przez zapytanie w swojej niestandardowej klasie pojo
źródło
Nie lubię nazw typów Java w ciągach zapytań i obsługuję je za pomocą określonego konstruktora. Spring JPA niejawnie wywołuje konstruktor z wynikiem zapytania w parametrze HashMap:
Kod potrzebuje Lomboka do rozpoznania @Getter
źródło
Właśnie rozwiązałem ten problem:
@Query(value = "SELECT ...", nativeQuery = true
)), więc zalecam zdefiniowanie niestandardowego DTO za pomocą interfejsu.źródło
Użyłem niestandardowego DTO (interfejsu) do odwzorowania natywnego zapytania na - najbardziej elastyczne podejście i bezpieczne dla refaktoryzacji.
Problem, który miałem z tym - to zaskakujące, kolejność pól w interfejsie i kolumny w zapytaniu mają znaczenie. Udało mi się to, porządkując alfabetycznie metody pobierające interfejs, a następnie ustawiając kolumny w zapytaniu w ten sam sposób.
źródło
Powyższy kod zadziałał dla mnie.
źródło