Spring Data PageImpl 没有返回正确大小的页面?

Posted

技术标签:

【中文标题】Spring Data PageImpl 没有返回正确大小的页面?【英文标题】:Spring Data PageImpl not returning page with the correct size? 【发布时间】:2014-12-30 11:52:05 【问题描述】:

我正在尝试使用从数据库中检索到的对象列表创建一个新的Page。首先,我从数据库中获取所有元素,将其转换为 Stream,然后使用 lambda 过滤结果。然后我需要一个包含一定数量元素的页面,但是,实例化一个新的 PageImpl 似乎并没有返回一个大小正确的页面。

这是我的代码:

List<Produtos> listaFinal;
Stream<Produtos> stream = produtosRepository.findAll().stream();
listaFinal = stream.filter(p -> p.getProdNome().contains("uio")).collect(Collectors.toList());

long total = listaFinal.size();
Page<Produtos> imp = new PageImpl<>(listaFinal,pageable,total);

这是调试的截图:

请注意,Pageable 对象中的大小设置为 20,它知道它需要 4 个页面来呈现 70 个元素,但它会返回整个列表。

我错过了什么?

编辑回答托马斯的评论:

我了解如何使用 Page 仅返回一部分数据。我展示的代码是我尝试使用 lambda 表达式来过滤我的集合。对我来说问题是我想使用 Java 8 的 lambda 通过 Spring Data JPA 查询数据库。我习惯了 VB.NET 和 Entity function(x) 查询表达式,想知道如何使用 Spring JPA 做同样的事情。

在我的存储库中,我使用extends JpaRepository&lt;Produtos, Integer&gt;, QueryDslPredicateExecutor&lt;Produtos&gt;,这使我可以访问findAll(Predicate,Pageable)。但是,谓词没有输入,所以我不能简单地在查询中使用p -&gt; p.getProdNome().contains("uio")。我正在使用 SQL Server 和 Hibernate。

【问题讨论】:

【参考方案1】:

在应用了很多方法之后,这是我的工作解决方案:

int pageSize = pageable.getPageSize();
long pageOffset = pageable.getOffset();
long total = pageOffset + list.size() + (list.size() == pageSize ? pageSize : 0);
Page<listType> page = new PageImpl<listType>(list, pageable,total)

【讨论】:

【参考方案2】:

我也遇到了同样的问题并找到了解决方法。

SimpleJpaRepository有方法:

public Page<T> findAll(Specification<T> spec, Pageable pageable) 
    TypedQuery<T> query = getQuery(spec, pageable);
    return pageable == null ? new PageImpl<T>(query.getResultList())
                : readPage(query, getDomainClass(), pageable, spec);

如果你扩展JpaRepository,它用于返回Page&lt;T&gt;。所以我们可以在这里使用相同的功能(需要重写代码,因为 Spring 没有给你提供公共方法来获得完整的分页支持)。

如果您查看PageImpl&lt;&gt;(List&lt;T&gt; content, Pageable pageable, long total); 方法,它只是设置了您在pageable 中给出的值。在这里,您将 content 作为完整列表发送,但 spring 不会出于内部目的这样做。

需要替换Page&lt;Produtos&gt; imp = new PageImpl&lt;&gt;(listaFinal,pageable,total);

以下代码:.

TypedQuery<User> query = entityManager.createQuery(criteriaQuery); 
// Users type can be replaced with any other entity

query.setFirstResult(pageable.getOffset());
query.setMaxResults(pageable.getPageSize());

List<User> users = query.getResultList();
Page<User> result = PageableExecutionUtils.getPage(users,pageable, 
                    () -> getCountForQuery(User.class));

方法getCountForQuery

private Long getCountForQuery(Class<?> t) 

    CriteriaBuilder criteriaBuilder=entityManager.getCriteriaBuilder();

    CriteriaQuery<Long> countQuery = criteriaBuilder
            .createQuery(Long.class);
    countQuery.select(criteriaBuilder.count(
            countQuery.from(t)));
    Long count = entityManager.createQuery(countQuery)
            .getSingleResult();

    return count;

PageableExecutionUtils.getPage的用法可以在:

readPage(TypedQuery<S> query, final Class<S> domainClass, 
         Pageable pageable, final Specification<S> spec) 

SimpleJpaRepository 中的方法,主要由findAll 内部方法使用。

【讨论】:

假设在数据查询中你有更多的根或连接,所以谓词可能涉及更多的实体。计数查询应该如何?...您不能仅从单个根执行计数查询...计数查询应该如何?...我还必须为计数查询重新定义所有根和连接? ...有什么想法吗?【参考方案3】:

要扩展stites' answer,可以使用PagedListHolder,方法如下:

List<String> list = // ...

// Creation
PagedListHolder page = new PagedListHolder(list);
page.setPageSize(10); // number of items per page
page.setPage(0);      // set to first page

// Retrieval
page.getPageCount(); // number of pages 
page.getPageList();  // a List which represents the current page

如果您需要排序,请使用另一个 PagedListHolder constructor 和 MutableSortDefinition。

【讨论】:

【参考方案4】:

PageImpl 不打算对您的列表执行任何类型的分页。 From the docs 你可以看到它只是“基本的Page 实现”,听起来几乎是你想要的,但它确实具有误导性。

使用PagedListHolder,它是一个简单的状态持有者,用于处理对象列表,将它们分成页面。

【讨论】:

【参考方案5】:

在了解有关 Spring Data 工作原理的更多信息后,我最终在 JpaRepository 实现中的方法上使用 @Query 注释来正确查询 DB 并过滤结果,从而无需使用流然后转换回 Page。

如果有人需要示例,以下是上述代码的外观:

@Query("select p from Produtos p where p.prodNome = ?1")
public Page<Produtos> productsListByName(String prodNome, Pageable pageable)

我知道 Spring 的 findBy 方法,但有时方法名称变得非常难以阅读,具体取决于参数的数量,所以我只是坚持使用 JPQL。

这样做,页面的内容将始终包含您在 Spring 配置中定义的最大数量的元素。

我还使用了PageImpl 的自定义实现,我现在不在工作,也无法访问代码,但我会尽可能发布。

编辑:自定义实现可以在here找到

【讨论】:

不错!我会用我正在做的事情来试一试。 您能否提供您一直用于 PageImpl 的代码。我现在面临同样的问题! 已添加代码。请注意,在我的情况下需要此实现,因为我正在处理 REST 服务,而默认实现对我不起作用,在您的情况下可能会有所不同。 将自定义实现移至另一个网站,因为另一个网站已关闭。 @dubonzi 如果查询是 typedQuery 怎么办......它将如何工作,在我的代码中它不起作用..***.com/questions/62485592/…【参考方案6】:

如果我对您的代码理解正确,那么您的意图是从数据库中加载所有记录并将它们拆分为 x 个存储桶,这些存储桶收集在 PageImpl 中,对吧?

以前不是这样的。 PageablePage 抽象的实际意图不是 必须查询所有数据,但只查询所需的“切片”数据。

在您的情况下,您可以通过Page&lt;X&gt; page = repository.findAll(pageable); 查询数据并简单地返回。 Page 保存当前页面的记录以及一些附加信息,例如记录总数以及是否有下一页。

在您的客户端代码中,您可以使用该信息来呈现记录列表并适当地生成下一个/上一个链接。 请注意,以Page&lt;X&gt; 作为结果类型的查询会发出 2 个查询(1 个用于确定查询的总计数,1 个用于确定实际页面数据)。

如果您不需要有关结果总数的信息,但仍希望能够生成下一个链接,您应该 使用 Slice&lt;X&gt; 作为返回类型 - 因为它只发出 1 个查询。

【讨论】:

我用我的评论编辑了我的原始问题,因为这里写得太长了,请看一下!谢谢。 我也面临同样的问题。我必须使用 DTO 来映射查询的输出以删除一些隐藏的列。所以我首先得到所有结果,然后根据 DTO 将结果映射到新的输出。然后我在新结果上使用 PageImpl 。但它显示所有记录而不是分页信息。

以上是关于Spring Data PageImpl 没有返回正确大小的页面?的主要内容,如果未能解决你的问题,请参考以下文章

Spring WebClient PageImpl Content 缺少字段

当列表分页的大小输入设置为大于 15 时,Spring Data PagedListHolder 没有返回正确的 PageSize,为啥?

spring data jpa @Query 枚举类型不返回数据

处理 JPA 规范和 spring-data-jpa 时如何使用声明 Stream 作为返回类型

如何使用spring-data-solr编写查询,返回特定字段的所有值的列表

Spring Data JPA 中的分页(限制和偏移)