Решение для запросов JPQL
Это поддерживается для запросов JPQL в спецификации JPA .
Шаг 1. Объявите простой класс bean-компонента
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;
}
}
Шаг 2. Верните экземпляры bean-компонентов из метода репозитория
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();
}
Важные заметки
- Обязательно укажите полный путь к классу компонента, включая имя пакета. Например, если вызывается класс компонента,
MyBean
и он находится в пакете com.path.to
, полный путь к компоненту будет com.path.to.MyBean
. Простое предоставление MyBean
не будет работать (если класс компонента не находится в пакете по умолчанию).
- Обязательно вызовите конструктор класса компонента с помощью
new
ключевого слова. SELECT new com.path.to.MyBean(...)
будет работать, тогда как SELECT com.path.to.MyBean(...)
не будет.
- Убедитесь, что атрибуты передаются в том же порядке, что и в конструкторе bean-компонента. Попытка передать атрибуты в другом порядке приведет к исключению.
- Убедитесь, что запрос является допустимым запросом JPA, то есть это не собственный запрос.
@Query("SELECT ...")
, или @Query(value = "SELECT ...")
, или @Query(value = "SELECT ...", nativeQuery = false)
будет работать, тогда как @Query(value = "SELECT ...", nativeQuery = true)
работать не будет. Это связано с тем, что собственные запросы передаются провайдеру JPA без изменений и выполняются для базовой СУБД как таковой. Поскольку new
и com.path.to.MyBean
не являются допустимыми ключевыми словами SQL, СУБД генерирует исключение.
Решение для собственных запросов
Как отмечалось выше, new ...
синтаксис является механизмом, поддерживаемым JPA, и работает со всеми поставщиками JPA. Однако, если сам запрос не является запросом JPA, то есть это собственный запрос, new ...
синтаксис не будет работать, поскольку запрос передается непосредственно в базовую СУБД, которая не понимаетnew
ключевое слово, поскольку оно не является частью стандарт SQL.
В подобных ситуациях классы компонентов необходимо заменить интерфейсами Spring Data Projection .
Шаг 1. Объявите интерфейс проекции
package com.path.to;
public interface SurveyAnswerStatistics {
String getAnswer();
int getCnt();
}
Шаг 2. Верните прогнозируемые свойства из запроса
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();
}
Используйте AS
ключевое слово SQL для сопоставления полей результатов со свойствами проекции для однозначного сопоставления.
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..........