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<Produtos, Integer>, QueryDslPredicateExecutor<Produtos>
,这使我可以访问findAll(Predicate,Pageable)
。但是,谓词没有输入,所以我不能简单地在查询中使用p -> 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<T>
。所以我们可以在这里使用相同的功能(需要重写代码,因为 Spring 没有给你提供公共方法来获得完整的分页支持)。
如果您查看PageImpl<>(List<T> content, Pageable pageable, long total);
方法,它只是设置了您在pageable
中给出的值。在这里,您将 content
作为完整列表发送,但 spring 不会出于内部目的这样做。
需要替换Page<Produtos> imp = new PageImpl<>(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
中,对吧?
以前不是这样的。 Pageable
和 Page
抽象的实际意图不是
必须查询所有数据,但只查询所需的“切片”数据。
在您的情况下,您可以通过Page<X> page = repository.findAll(pageable);
查询数据并简单地返回。
Page 保存当前页面的记录以及一些附加信息,例如记录总数以及是否有下一页。
在您的客户端代码中,您可以使用该信息来呈现记录列表并适当地生成下一个/上一个链接。
请注意,以Page<X>
作为结果类型的查询会发出 2 个查询(1 个用于确定查询的总计数,1 个用于确定实际页面数据)。
如果您不需要有关结果总数的信息,但仍希望能够生成下一个链接,您应该
使用 Slice<X>
作为返回类型 - 因为它只发出 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 作为返回类型