Spring Boot 分页和过滤器示例

2022年05月13日 阅读数:4
这篇文章主要向大家介绍Spring Boot 分页和过滤器示例,主要内容包括基础应用、实用技巧、原理机制等方面,希望对大家有所帮助。

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

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

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

假设咱们在数据库中有tutorials表,以下所示:java

 

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

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

这是咱们但愿从 API 得到的服务器端分页结果的结构:github

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

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

 

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

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

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

 

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

 

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

 

按发布状态分页和过滤:

 

使用 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上找到本教程的完整源代码。