Solución para consultas JPQL
Esto es compatible con consultas JPQL dentro de la especificación JPA .
Paso 1 : declarar una clase de frijol simple
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;
}
}
Paso 2 : devolver instancias de bean desde el método de repositorio
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();
}
Notas importantes
- Asegúrese de proporcionar la ruta completa a la clase de bean, incluido el nombre del paquete. Por ejemplo, si se llama a la clase de bean
MyBean
y está en el paquete com.path.to
, la ruta completa al bean será com.path.to.MyBean
. Simplemente proporcionar MyBean
no funcionará (a menos que la clase de bean esté en el paquete predeterminado).
- Asegúrese de llamar al constructor de la clase bean usando la
new
palabra clave. SELECT new com.path.to.MyBean(...)
funcionará, mientras SELECT com.path.to.MyBean(...)
que no.
- Asegúrese de pasar los atributos exactamente en el mismo orden que el esperado en el constructor del bean. Si intenta pasar atributos en un orden diferente, se producirá una excepción.
- Asegúrese de que la consulta sea una consulta JPA válida, es decir, que no sea una consulta nativa.
@Query("SELECT ...")
O @Query(value = "SELECT ...")
, o @Query(value = "SELECT ...", nativeQuery = false)
va a trabajar, mientras que @Query(value = "SELECT ...", nativeQuery = true)
no va a funcionar. Esto se debe a que las consultas nativas se pasan sin modificaciones al proveedor JPA y se ejecutan contra el RDBMS subyacente como tal. Dado que new
y com.path.to.MyBean
no son palabras clave SQL válidas, RDBMS lanza una excepción.
Solución para consultas nativas
Como se señaló anteriormente, la new ...
sintaxis es un mecanismo compatible con JPA y funciona con todos los proveedores de JPA. Sin embargo, si la consulta en sí no es una consulta JPA, es decir, es una consulta nativa, la new ...
sintaxis no funcionará ya que la consulta se pasa directamente al RDBMS subyacente, que no comprende la new
palabra clave ya que no es parte de el estándar SQL.
En situaciones como estas, las clases de bean deben reemplazarse con interfaces Spring Data Projection .
Paso 1 : declare una interfaz de proyección
package com.path.to;
public interface SurveyAnswerStatistics {
String getAnswer();
int getCnt();
}
Paso 2 : devolver las propiedades proyectadas de la consulta
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();
}
Utilice la AS
palabra clave SQL para asignar campos de resultados a propiedades de proyección para un mapeo inequívoco.
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
en su salida? Supongo que lo reemplazóSurveyAnswerStatistics
con su propia claseSurveyAnswerReport
. Debe especificar el nombre de la clase completamente calificado.com.domain.dto.SurveyAnswerReport
.JpaRepository
? ¿Me perdí alguna configuración?Esta consulta SQL devolvería la Lista <Objeto []>.
Puedes hacerlo de esta manera:
fuente
Sé que esta es una pregunta antigua y ya ha sido respondida, pero aquí hay otro enfoque:
fuente
Usando interfaces puede obtener un código más simple. No es necesario crear y llamar manualmente a los constructores
Paso 1 : Declare intefrace con los campos obligatorios:
Paso 2 : seleccione columnas con el mismo nombre que el captador en la interfaz y devuelva la interfaz desde el método del repositorio:
fuente
defina una clase personalizada de pojo, digamos sureveyQueryAnalytics y almacene el valor devuelto por la consulta en su clase personalizada de pojo
fuente
No me gustan los nombres de tipo java en las cadenas de consulta y los manejo con un constructor específico. Spring JPA llama implícitamente al constructor con el resultado de la consulta en el parámetro HashMap:
El código necesita Lombok para resolver @Getter
fuente
Acabo de resolver este problema:
@Query(value = "SELECT ...", nativeQuery = true
)), por lo que recomiendo definir DTO personalizado mediante la interfaz.fuente
Utilicé DTO (interfaz) personalizado para asignar una consulta nativa a: el enfoque más flexible y seguro para la refactorización.
El problema que tuve con esto es que, sorprendentemente, el orden de los campos en la interfaz y las columnas en la consulta es importante. Lo hice funcionar ordenando los captadores de interfaz alfabéticamente y luego ordenando las columnas en la consulta de la misma manera.
fuente
El código anterior funcionó para mí.
fuente