使用动态查询的Spring Data JPA分页(Pageable)

22
我有一个简单的查询,如下所示:“select * from USERS”。我还使用Pageable来启用分页。
该查询可能基于给定参数是否为空而具有可选谓词。
例如,如果给定“code”参数不为null,则查询变为“select * from USERS where code = :code”;
据我所知,我不能使用@Query注释来实现这一点。我可以实现自定义存储库并使用EntityManager创建动态查询。 但是,我不确定如何将“Pageable”与其集成以获取分页结果。
我该如何实现这一点?
4个回答

16

使用QueryDSL(作为Criteria API的替代品),在Spring Data中非常容易实现这一点。它通过QueryDSLPredicateExecutor的以下方法支持开箱即用,如果不需要应用任何限制,只需将Predicate传递为null即可:

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

如果您不能使用QueryDSL,但是如果您查看以下一系列教程,您可能会得到一些想法。

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

实际上,文中的作者在他指南的第9部分评论中讨论了您所面临的情况。


1
现在使用QueryDslRepositorySupport时,您仍然可以访问此功能,因为它使用 #getQuerydsl() 公开了底层的querydsl实例。 - chrismarx

4

对于querydsl查询获取页面结果有一定的复杂性,因为你需要两个查询:一个用于获取条目总数,另一个用于获取页面中所需的条目列表。您可以使用以下超类:

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);
  }

}

但是 applyPagination 方法使用 JPQLQuery 对象。 - Tisha
1
这样不会执行两次查询吗--一次获取结果,一次获取计数?那不太好。 - Josh M.
@JoshM:一般来说,这就是分页的工作原理。 - Priidu Neemre

1

您需要使用querydsl,并根据非空参数构建您的where,例如

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

执行查询后

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

并使用您自己的页面。
    MaPage<YOURENTITY> page = new MaPage<YOURENTITY>();
    page.number = pageNumber+1;

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

    page.totalResult = query.count();

我这样创建了MyPage。
public class MaPage<T> {

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

这个方法可以工作,但如果你的查询中包含了fetch语句,那么就会出现这个警告:

nov. 21, 2014 6:48:54 AM org.hibernate.hql.internal.ast.QueryTranslatorImpl list
WARN: HHH000104: firstResult/maxResults specified with collection fetch; applying in memory!

这会拖慢你的请求速度。所以解决方案是去掉fetch,并定义一个@BatchSize(size=10),使用Hibernate.initialize(....)来获取集合和其他类型的数据。

显示相关实体的数据,避免使用@BatchSize时的延迟初始化异常

如何使用Spring Data和QueryDSL执行带有分页的JPA查询


0

看起来你的链接已经过时了。 - jediwompa

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接