【问题标题】:Spring Data PageImpl not returning page with the correct size?Spring Data PageImpl 没有返回正确大小的页面?
【发布时间】: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。

【问题讨论】:

    标签: java spring spring-mvc spring-data spring-data-jpa


    【解决方案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 constructorMutableSortDefinition

      【讨论】:

        【解决方案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 怎么办......它将如何工作,在我的代码中它不起作用..stackoverflow.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 。但它显示所有记录而不是分页信息。
          猜你喜欢
          • 2019-01-20
          • 1970-01-01
          • 2015-07-31
          • 2020-11-06
          • 2021-12-11
          • 2021-03-13
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多