Solution pour les requêtes JPQL
Ceci est pris en charge pour les requêtes JPQL dans la spécification JPA .
Étape 1 : Déclarez une classe de bean 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;
}
}
Étape 2 : renvoyer les instances de bean à partir de la méthode du référentiel
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();
}
Notes IMPORTANTES
- Assurez-vous de fournir le chemin d'accès complet à la classe du bean, y compris le nom du package. Par exemple, si la classe du bean est appelée
MyBean
et qu'elle est dans le package com.path.to
, le chemin d'accès complet au bean sera com.path.to.MyBean
. Le simple fait de fournir MyBean
ne fonctionnera pas (sauf si la classe du bean est dans le package par défaut).
- Assurez-vous d'appeler le constructeur de classe bean en utilisant le
new
mot - clé. SELECT new com.path.to.MyBean(...)
fonctionnera, alors que SELECT com.path.to.MyBean(...)
non.
- Assurez-vous de passer les attributs exactement dans le même ordre que celui attendu dans le constructeur du bean. Tenter de transmettre les attributs dans un ordre différent entraînera une exception.
- Assurez-vous que la requête est une requête JPA valide, c'est-à-dire qu'il ne s'agit pas d'une requête native.
@Query("SELECT ...")
, ou @Query(value = "SELECT ...")
, ou @Query(value = "SELECT ...", nativeQuery = false)
fonctionnera, alors que @Query(value = "SELECT ...", nativeQuery = true)
ne fonctionnera pas. Cela est dû au fait que les requêtes natives sont transmises sans modifications au fournisseur JPA et sont exécutées sur le SGBDR sous-jacent en tant que tel. Puisque new
et com.path.to.MyBean
ne sont pas des mots clés SQL valides, le SGBDR lève alors une exception.
Solution pour les requêtes natives
Comme indiqué ci-dessus, la new ...
syntaxe est un mécanisme pris en charge par JPA et fonctionne avec tous les fournisseurs JPA. Cependant, si la requête elle-même n'est pas une requête JPA, c'est-à-dire une requête native, la new ...
syntaxe ne fonctionnera pas car la requête est transmise directement au SGBDR sous-jacent, qui ne comprend pas lenew
mot clé car il ne fait pas partie de la norme SQL.
Dans de telles situations, les classes de bean doivent être remplacées par des interfaces Spring Data Projection .
Étape 1 : déclarer une interface de projection
package com.path.to;
public interface SurveyAnswerStatistics {
String getAnswer();
int getCnt();
}
Étape 2 : renvoyer les propriétés projetées à partir de la requête
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();
}
Utilisez le AS
mot clé SQL pour mapper les champs de résultat aux propriétés de projection pour un mappage sans ambiguïté.
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
dans votre sortie. Je suppose que vous avez remplacéSurveyAnswerStatistics
par votre propre classeSurveyAnswerReport
. Vous devez spécifier le nom complet de la classe.com.domain.dto.SurveyAnswerReport
.JpaRepository
? Une configuration me manque-t-elle?Cette requête SQL renvoie List <Object []>.
Vous pouvez le faire de cette façon:
la source
Je sais que c'est une vieille question à laquelle on a déjà répondu, mais voici une autre approche:
la source
En utilisant des interfaces, vous pouvez obtenir un code plus simple. Pas besoin de créer et d'appeler manuellement des constructeurs
Étape 1 : Déclarez l'intégrale avec les champs obligatoires:
Étape 2 : Sélectionnez les colonnes portant le même nom que getter dans l'interface et renvoyez l'interface à partir de la méthode du référentiel:
la source
définir une classe pojo personnalisée, dire sureveyQueryAnalytics et stocker la valeur renvoyée par la requête dans votre classe pojo personnalisée
la source
Je n'aime pas les noms de type java dans les chaînes de requête et je les gère avec un constructeur spécifique. Spring JPA appelle implicitement le constructeur avec le résultat de la requête dans le paramètre HashMap:
Le code a besoin de Lombok pour résoudre @Getter
la source
Je viens de résoudre ce problème:
@Query(value = "SELECT ...", nativeQuery = true
)), je recommande donc de définir un DTO personnalisé à l'aide de l'interface.la source
J'ai utilisé un DTO (interface) personnalisé pour mapper une requête native vers - l'approche la plus flexible et la refactorisation sûre.
Le problème que j'ai eu avec cela - que, étonnamment, l'ordre des champs dans l'interface et les colonnes dans la requête est important. Je l'ai fait fonctionner en ordonnant les getters d'interface par ordre alphabétique, puis en ordonnant les colonnes de la requête de la même manière.
la source
Le code ci-dessus a fonctionné pour moi.
la source