J'essaie d'utiliser HQL pour récupérer mon entité avec des sous-entités à l'aide de JOIN FETCH, cela fonctionne très bien si je veux tous les résultats mais ce n'est pas le cas si je veux une page
Mon entité est
@Entity
@Data
public class VisitEntity {
@Id
@Audited
private long id;
.
.
.
@OneToMany(cascade = CascadeType.ALL,)
private List<VisitCommentEntity> comments;
}
et parce que j'ai des millions de visites, je dois utiliser Pageable et je veux récupérer les commentaires dans une seule requête de base de données comme:
@Query("SELECT v FROM VisitEntity v LEFT JOIN FETCH v.comments WHERE v.venue.id = :venueId and ..." )
public Page<VisitEntity> getVenueVisits(@Param("venueId") long venueId,...,
Pageable pageable);
Cet appel HQL lève l'exception suivante:
Caused by: Java.lang.IllegalArgumentException: org.hibernate.QueryException: query specified join fetching, but the owner of the fetched association was not present in the select list [FromElement{explicit,not a collection join,fetch join,fetch non-lazy properties,classAlias=null,role=com.ro.lib.visit.entity.VisitEntity.comments,tableName=visitdb.visit_comment,tableAlias=comments1_,Origin=visitdb.visit visitentit0_,columns={visitentit0_.visit_id ,className=com.ro.lib.visit.entity.VisitCommentEntity}}] [select count(v) FROM com.ro.lib.visit.entity.VisitEntity v LEFT JOIN FETCH v.comments WHERE v.venue.id = :venueId and (v.actualArrival > :date or v.arrival > :date)]
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.Java:1374)
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.Java:1310)
at org.hibernate.ejb.AbstractEntityManagerImpl.createQuery(AbstractEntityManagerImpl.Java:309)
at Sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
et une fois que j'ai retiré la pagination, tout fonctionne bien
@Query("SELECT v FROM VisitEntity v LEFT JOIN FETCH v.comments WHERE v.venue.id = :venueId and ..." )
public List<VisitEntity> getVenueVisits(@Param("venueId") long venueId,...);
De toute évidence, le problème est la requête de comptage de Spring-Data, mais comment pouvons-nous y remédier?
La manière la plus simple consiste à utiliser l'attribut countQuery
du @Query
annotation pour fournir une requête personnalisée à utiliser.
@Query(value = "SELECT v FROM VisitEntity v LEFT JOIN FETCH v.comments …",
countQuery = "select count(v) from VisitEntity v where …")
List<VisitEntity> getVenueVisits(@Param("venueId") long venueId, …);
Dans les versions les plus récentes de Spring (prenant en charge la spécification JPA 2.1), vous pouvez également utiliser un graphique d'entité comme celui-ci:
@EntityGraph(attributePaths = "roles")
@Query("FROM User user")
Page<User> findAllWithRoles(Pageable pageable);
Bien sûr, les graphes d'entités nommées fonctionnent également.
Vous devez spécifier countQuery
param pour @Query
et maintenant vous pouvez utiliser Page
ou List
comme valeur de retour.
@Query(value = "SELECT v FROM VisitEntity v LEFT JOIN FETCH v.comments WHERE v.venue.id = :venueId and ...",
countQuery = "SELECT count(v) FROM VisitEntity v LEFT JOIN v.comments WHERE v.venue.id = :venueId and ..." )
public Page<VisitEntity> getVenueVisits(@Param("venueId") long venueId,...,
Pageable pageable);
Essayez countProjection
@Query(value="SELECT v FROM VisitEntity v LEFT JOIN FETCH v.comments WHERE v.venue.id = :venueId and ..." ,
countProjection = "v.id")
public Page<VisitEntity> getVenueVisits(@Param("venueId") long venueId,...,
Pageable pageable);