web-dev-qa-db-fra.com

Spring-Data FETCH JOIN avec pagination ne fonctionne pas

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?

28
Ehab Al-Hakawati

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, …);
52
Oliver Drotbohm

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.

5
Piotr Tempes

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);
2
Max

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);
0
heobo