web-dev-qa-db-fra.com

Pagination JPA de données de printemps (paginable) avec requêtes dynamiques

J'ai une requête simple comme suit "sélectionnez * des utilisateurs". J'utilise aussi Pageable pour activer la pagination.

Cette requête peut avoir des prédicats facultatifs basés sur des paramètres nuls ou nuls.

Par exemple, si le paramètre "code" est donné et non null, la requête devient "Select * from USERS où code =: code";

Autant que je sache, je ne peux pas l'implémenter avec l'annotation @Query. Je peux implémenter un référentiel personnalisé et utiliser EntityManager pour créer une requête dynamique . Cependant, je ne sais pas comment intégrer "Pageable" à celui-ci pour obtenir des résultats paginés.

Comment puis-je atteindre cet objectif? 

14
led

Ceci est très facile à faire dans Spring Data en utilisant QueryDSL (comme alternative à l’API critère). Il est pris en charge immédiatement avec la méthode suivante de QueryDSLPredicateExecutor, dans laquelle vous pouvez simplement passer null comme prédicat si aucune restriction ne doit être appliquée:

Page<T> findAll(com.mysema.query.types.Predicate predicate,
                Pageable pageable)

QueryDSL n'est peut-être pas une option pour vous. Toutefois, si vous regardez la série de didacticiels suivante, vous aurez peut-être des idées.

http://www.petrikainulainen.net/programming/spring-framework/spring-data-jpa-tutorial-part-nine-conclusions/

Le scénario que vous avez est en fait discuté par l'auteur dans les commentaires de la partie 9 de son guide.

14
Alan Hay

Obtenir les résultats de page pour les requêtes querydsl est quelque peu compliqué, car vous avez besoin de deux requêtes: une pour le nombre total d'entrées et l'autre pour la liste des entrées dont vous avez besoin dans la page . Vous pouvez utiliser la superclasse suivante:

public class QueryDslSupport<E, Q extends EntityPathBase<E>> extends QueryDslRepositorySupport {

  public QueryDslSupport(Class<E> clazz) {
    super(clazz);
  }

  protected Page<E> readPage(JPAQuery query, Q qEntity, Pageable pageable) {
    if (pageable == null) {
      return readPage(query, qEntity, new QPageRequest(0, Integer.MAX_VALUE));
    }
    long total = query.clone(super.getEntityManager()).count(); // need to clone to have a second query, otherwise all items would be in the list
    JPQLQuery pagedQuery = getQuerydsl().applyPagination(pageable, query);
    List<E> content = total > pageable.getOffset() ? pagedQuery.list(qEntity) : Collections.<E> emptyList();
    return new PageImpl<>(content, pageable, total);
  }

}
4
Sebastian

Vous devez utiliser querydsl et construire votre where en fonction du paramètre not null, par exemple 

BooleanBuilder where = new BooleanBuilder();
...
    if(code  != null){
        where.and(YOURENTITY.code.eq(code));
    } 

et après exécuter la requête

    JPAQuery query = new JPAQuery(entityManager).from(..)               
            .leftJoin( .. )
            ...
            .where(where)

et utilisez votre propre page

    MaPage<YOURENTITY> page = new MaPage<YOURENTITY>();
    page.number = pageNumber+1;

    page.content = query.offset(pageNumber*pageSize).limit(pageSize).list(...);

    page.totalResult = query.count();

Je crée MyPage comme ça

public class MaPage<T> {

    public List<T> content;
    public int number;
    public Long totalResult;
    public Long totalPages;
    ...
}

cela fonctionne, mais si dans votre requête, vous avez un aller chercher, alors tu vas avoir cet avertissement

nov. 21, 2014 06:48:54 org.hibernate.hql.internal.ast.QueryTranslatorImpl list
WARN: HHH000104: firstResult/maxResults spécifiés avec la récupération de collection; appliquer en mémoire!

et cela ralentira votre demande. La solution consiste donc à contourner le processus d’extraction, à définir une fonction @BatchSize(size=10) et à utiliser Hibernate.initialize(....) pour récupérer des données dans des collections et d’autres types d’objets.

Affichez les données des entités associées pour éviter l'exception d'initialisation différée lors de la configuration de @BatchSize

Comment exécuter une requête JPAQuery avec une pagination à l'aide de Spring Data et QueryDSL

1
Youssef

Les informations ici sont obsolètes. Demandez à votre référentiel d'implémenter/ QueryDslPredicateExecutor et la pagination est gratuite. 

0
Steve Edgar