Spring Boot 分页和过滤器示例

在上一篇文章中,我们已经知道如何使用 Spring Data JPA 构建 Spring Boot Rest CRUD APIs。在本教程中,我将继续使用 Spring Data JPA 和Pageable制作服务器端分页和过滤器。

Spring Boot 分页和过滤器示例概述

使网站友好的最重要的事情之一是响应时间,而分页就是这个原因。例如,这个 bezkoder.com 网站有数百个教程,我们不想一次看到所有教程。分页是指以一页为单位显示全部的一小部分。

假设我们在数据库中有tutorials表,如下所示:

 

以下是一些用于分页的 url 示例(带/不带过滤器):

  • /api/tutorials?page=1&size=5
  • /api/tutorials?size=5: 使用页面的默认值
  • /api/tutorials?title=data&page=1&size=3:按包含“数据”的标题进行分页和过滤
  • /api/tutorials/published?page=2:按“已发布”状态进行分页和过滤

这是我们希望从 API 获得的服务器端分页结果的结构:

{
    "totalItems": 8,
    "tutorials": [...],
    "totalPages": 3,
    "currentPage": 1
}

阅读具有默认页面索引 (0) 和页面大小 (3) 的教程:

 

指示页面索引 = 2,但未指定总共 8 个项目的大小(默认值:3):

  • page_0:3 项
  • page_1:3 项
  • page_2:2 项

指示 size = 5 但不指定页面索引(默认值:0):

 

对于页面索引 = 1 和页面大小 = 5(总共 8 项):

 

按包含字符串的标题进行分页和过滤:

 

按发布状态分页和过滤:

 

使用 Spring Data JPA 进行分页和过滤

为了帮助我们处理这种情况,Spring Data JPA 提供了使用PagingAndSortingRepository实现分页的方法。

PagingAndSortingRepository扩展CrudRepository以提供额外的方法来使用分页抽象检索实体。

public interface PagingAndSortingRepository<T, ID> extends CrudRepository<T, ID> {
  Page<T> findAll(Pageable pageable);
}

findAll(Pageable pageable):返回Page满足对象提供的分页条件的实体Pageable

Spring Data 还支持从方法名称创建许多有用的查询,我们将在此示例中使用这些名称来过滤结果,例如:

Page<Tutorial> findByPublished(boolean published, Pageable pageable);
Page<Tutorial> findByTitleContaining(String title, Pageable pageable);

您可以在此处的方法名称中找到更多受支持的关键字。

要使用分页对多个字段进行排序,请访问教程:
Spring Data JPA 按多列排序/排序 | 弹簧靴

春季数据页面

让我们看一下Page对象。

PageSlice带有几个附加方法的子接口。它包含整个列表的元素总数和总页数。

public interface Page<T> extends Slice<T> {
  static <T> Page<T> empty();
  static <T> Page<T> empty(Pageable pageable);
  long getTotalElements();
  int getTotalPages();
  <U> Page<U> map(Function<? super T,? extends U> converter);
}

如果项目数量增加,性能可能会受到影响,是时候考虑Slice了。

一个Slice对象比 a 知道的信息少Page,例如,下一个或前一个是否可用,或者这个切片是第一个/最后一个。当您不需要项目总数和总页数时,您可以使用它。

public interface Slice<T> extends Streamable<T> {
  int getNumber();
  int getSize();
  int getNumberOfElements();
  List<T> getContent();
  boolean hasContent();
  Sort getSort();
  boolean isFirst();
  boolean isLast();
  boolean hasNext();
  boolean hasPrevious();
  ...
}

Spring 数据可分页

现在我们将在上面的 Repository 方法中看到Pageable参数。Spring Data 基础设施将自动识别此参数以将分页和排序应用于数据库。

Pageable界面包含有关所请求页面的信息,例如页面的大小和数量。

public interface Pageable {
  int getPageNumber();
  int getPageSize();
  long getOffset();
  Sort getSort();
  Pageable next();
  Pageable previousOrFirst();
  Pageable first();
  boolean hasPrevious();
  ...
}

所以当我们想在结果中获得分页(有或没有过滤器)时,我们只需添加Pageable到方法的定义中作为参数。

Page<Tutorial> findAll(Pageable pageable);
Page<Tutorial> findByPublished(boolean published, Pageable pageable);
Page<Tutorial> findByTitleContaining(String title, Pageable pageable);

这就是我们使用实现接口的PageRequestPageable类创建对象的方式:Pageable

Pageable paging = PageRequest.of(page, size);
  • page:从零开始的页面索引,不得为负数。
  • size:要返回的页面中的项目数,必须大于0。

创建 Spring Boot 应用程序

你可以一步一步来,或者在这篇文章中获取源代码:
Spring Boot, Spring Data JPA – Rest CRUD API example

Spring 项目包含我们只需要添加一些更改以使分页正常工作的结构。

或者您可以在本教程的最后获取新的 Github 源代码(包括分页和排序)。

数据模型

我们有这样的教程实体:

package com.bezkoder.spring.data.jpa.pagingsorting.model;
import javax.persistence.*;
@Entity
@Table(name = "tutorials")
public class Tutorial {
  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  private long id;
  @Column(name = "title")
  private String title;
  @Column(name = "description")
  private String description;
  @Column(name = "published")
  private boolean published;
  public Tutorial() {
  }
  public Tutorial(String title, String description, boolean published) {
    this.title = title;
    this.description = description;
    this.published = published;
  }
  public long getId() {
    return id;
  }
  public String getTitle() {
    return title;
  }
  public void setTitle(String title) {
    this.title = title;
  }
  public String getDescription() {
    return description;
  }
  public void setDescription(String description) {
    this.description = description;
  }
  public boolean isPublished() {
    return published;
  }
  public void setPublished(boolean isPublished) {
    this.published = isPublished;
  }
  @Override
  public String toString() {
    return "Tutorial [id=" + id + ", title=" + title + ", desc=" + description + ", published=" + published + "]";
  }
}

支持分页和过滤器的存储库

在本教程的早期,我们知道PagingAndSortingRepository,但在本示例中,为了保持连续性并利用 Spring Data JPA,我们继续使用扩展接口的JpaRepository 。PagingAndSortingRepository

package com.bezkoder.spring.data.jpa.pagingsorting.repository;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import com.bezkoder.spring.data.jpa.pagingsorting.model.Tutorial;
public interface TutorialRepository extends JpaRepository<Tutorial, Long> {
  Page<Tutorial> findByPublished(boolean published, Pageable pageable);
  Page<Tutorial> findByTitleContaining(String title, Pageable pageable);
}

pageable在上面的代码中,我们使用带有 Spring Query Creation的add参数来查找所有标题包含输入字符串的教程。

更多派生查询:
Spring Boot 中的 JPA 存储库查询示例

@Query注释的自定义查询:
Spring JPA @Query 示例:Spring Boot 中的自定义查询

带有分页和过滤器的控制器

通常,在 HTTP 请求 URL 中,分页参数是可选的。因此,如果我们的 Rest API 支持服务器端分页,我们应该提供默认值以使分页工作,即使客户端没有指定这些参数。

package com.bezkoder.spring.data.jpa.pagingsorting.controller;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
...
@RestController
@RequestMapping("/api")
public class TutorialController {
  @Autowired
  TutorialRepository tutorialRepository;
  @GetMapping("/tutorials")
  public ResponseEntity<Map<String, Object>> getAllTutorials(
        @RequestParam(required = false) String title,
        @RequestParam(defaultValue = "0") int page,
        @RequestParam(defaultValue = "3") int size
      ) {
    try {
      List<Tutorial> tutorials = new ArrayList<Tutorial>();
      Pageable paging = PageRequest.of(page, size);
      
      Page<Tutorial> pageTuts;
      if (title == null)
        pageTuts = tutorialRepository.findAll(paging);
      else
        pageTuts = tutorialRepository.findByTitleContaining(title, paging);
      tutorials = pageTuts.getContent();
      Map<String, Object> response = new HashMap<>();
      response.put("tutorials", tutorials);
      response.put("currentPage", pageTuts.getNumber());
      response.put("totalItems", pageTuts.getTotalElements());
      response.put("totalPages", pageTuts.getTotalPages());
      return new ResponseEntity<>(response, HttpStatus.OK);
    } catch (Exception e) {
      return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR);
    }
  }
  
  @GetMapping("/tutorials/published")
  public ResponseEntity<Map<String, Object>> findByPublished(
        @RequestParam(defaultValue = "0") int page,
        @RequestParam(defaultValue = "3") int size
      ) {
    try {      
      List<Tutorial> tutorials = new ArrayList<Tutorial>();
      Pageable paging = PageRequest.of(page, size);
      
      Page<Tutorial> pageTuts = tutorialRepository.findByPublished(true, paging);
      tutorials = pageTuts.getContent();
            
      Map<String, Object> response = new HashMap<>();
      response.put("tutorials", tutorials);
      response.put("currentPage", pageTuts.getNumber());
      response.put("totalItems", pageTuts.getTotalElements());
      response.put("totalPages", pageTuts.getTotalPages());
      
      return new ResponseEntity<>(response, HttpStatus.OK);
    } catch (Exception e) {
      return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
    }
  }
  ...
}

@RequestParam在上面的代码中,我们使用注释来接受分页参数pagesize。默认情况下,3教程将从页面索引中的数据库中获取0

接下来,我们用&创建一个Pageable对象。然后检查参数是否存在。pagesize
title

  • 如果为null,我们调用Repository findAll(paging),分页就是Pageable上面的对象。
  • 如果客户端使用 发送请求,请title使用findByTitleContaining(title, paging)

两种方法都返回一个Page对象。我们称之为:

  • getContent()检索页面中的项目列表。
  • getNumber()当前页面。
  • getTotalElements()对于存储在数据库中的总项目。
  • getTotalPages()总页数。

结论

在这篇文章中,我们学习了如何使用 Spring Data JPA、Page 和 Pageable 接口在 Spring Boot 应用程序中创建分页和过滤结果。

我们还看到它JpaRepository支持一种无需样板代码即可制作服务器端分页和过滤方法的好方法。

 

 

源代码

您可以在Github上找到本教程的完整源代码。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值