使用Spring Boot进行分页

为 Web 应用程序的用户,我们希望页面能够快速加载并且只显示 与我们相关的信息。对于显示项目列表的页面,这意味着 仅显示部分项目,而不是一次显示所有项目

快速加载第一页后,UI 可以提供筛选器、 排序和分页,帮助用户快速找到他或 她正在寻找。

在本教程中,我们将检查 Spring Data 的分页支持,并创建如何使用的示例 并对其进行配置以及有关其如何在幕后工作的一些信息。

示例代码

本文附有GitHub 上的工作代码示例。

分页与分页

术语“分页”和“分页”通常用作同义词。他们的意思并不完全相同, 然而。在查阅了各种网络词典后,我拼凑出了以下定义, 我将在本文中使用:

页是从数据库中加载一页又一页项目的行为,以便 保护资源。这就是本文大部分内容的内容。

分页是一个 UI 元素,它提供一系列页码,让用户选择 接下来要加载的页面。

初始化示例项目

在本教程中,我们使用 Spring Boot 来引导一个项目。您可以 通过使用Spring Initializr并选择以下依赖项来创建类似的项目:

  • 太平绅士
  • H2
  • 龙目岛

我还用 JUnit 5 替换了 JUnit 4,以便生成的依赖项 看起来像这样(Gradle 表示法):

dependencies {
  implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
  implementation 'org.springframework.boot:spring-boot-starter-web'
  compileOnly 'org.projectlombok:lombok'
  annotationProcessor 'org.projectlombok:lombok'
  runtimeOnly 'com.h2database:h2'
  testImplementation('org.junit.jupiter:junit-jupiter:5.4.0')
  testImplementation('org.springframework.boot:spring-boot-starter-test'){
    exclude group: 'junit', module: 'junit'
  }
}

春季数据Pageable

无论我们想做传统的分页,无限滚动还是简单的“上一个” 和“下一个”链接,后端的实现是相同的。

如果客户端只想显示项目列表的“切片”,则需要提供 描述此切片的一些输入参数。在 Spring 数据中,这些参数捆绑在 界面。它提供了以下方法,其中包括(评论是我的):Pageable

public interface Pageable {
    
  // number of the current page   int getPageNumber();
  
  // size of the pages   int getPageSize();
  
  // sorting parameters   Sort getSort();
    
  // ... more methods }

每当我们只想加载完整项目列表的一部分时,我们都可以使用 ainstance 作为输入参数,因为它提供 要加载的页面编号以及页面大小。通过课堂, 它还允许定义要排序的字段及其方向 应排序(升序或降序)。PageableSort

创建实例的最常见方法是使用实现:PageablePageRequest

Pageable pageable = PageRequest.of(0, 5, Sort.by(
    Order.asc("name"),
    Order.desc("id")));

这将为第一页创建一个请求,其中首先订购了 5 个项目 按名称(升序),按 ID (降序)。请注意,默认情况下页面索引是从零开始的!

困惑?java.awt.print.Pageable

使用时,您会注意到 IDE 有时会 建议进口代替春天的班。由于我们很可能不需要包中的任何类,因此我们可以 告诉我们的 IDE 完全忽略它。Pageablejava.awt.print.PageablePageablejava.awt

在IntelliJ中,转到设置中的“常规->编辑器->自动导入”,然后添加到标有“从导入和完成中排除”的列表中。java.awt.*

在 Eclipse 中,转到首选项中的“Java -> 外观 -> 类型过滤器”,然后 添加到包列表中。java.awt.*

春天的数据沙子PageSlice

同时捆绑分页请求的输入参数,与接口 为返回给客户端的项目页面提供元数据(评论是我的):PageablePageSlice

public interface Page<T> extends Slice<T>{
  
  // total number of pages   int getTotalPages();
  
  // total number of items   long getTotalElements();
  
  // ... more methods   
}
public interface Slice<T> {
  
  // current page number   int getNumber();
    
  // page size   int getSize();
    
  // number of items on the current page   int getNumberOfElements();
    
  // list of items on this page   List<T> getContent();
  
  // ... more methods   
}

通过接口提供的数据,客户端拥有所需的所有信息 以提供分页功能。Page

我们可以改用接口,如果我们不这样做 需要项目或页面的总数,例如,如果我们只想提供 “上一页”和“下一页”按钮,不需要“第一页”和“最后一页” 按钮。Slice

接口最常见的实现由类提供:PagePageImpl

Pageable pageable = ...;
List<MovieCharacter> listOfCharacters = ...;
long totalCharacters = 100;
Page<MovieCharacter> page = 
    new PageImpl<>(listOfCharacters, pageable, totalCharacters);

Web 控制器中的分页

如果我们想在 Web 控制器中返回一个(或)项,它需要接受 定义分页参数的参数,将其传递给数据库, ,然后将 aobject 返回给客户端。PageSlicePageablePage

激活弹簧数据网络支持

分页必须由底层持久性层支持,以便 为任何查询提供分页答案。这就是为什么分页页面类源自 Spring 数据模块,而不是像人们可能的那样。 可疑,来自Spring Web模块。

在启用了自动配置(这是默认设置)的 Spring 启动应用程序中,我们没有 做任何事情,因为它将加载默认情况下,这 包括加载必要 Bean 的注释。SpringDataWebAutoConfiguration@EnableSpringDataWebSupport

在没有 Spring Boot 的普通 Spring 应用程序中,我们必须自己在类上使用:@EnableSpringDataWebSupport@Configuration

@Configuration
@EnableSpringDataWebSupport
class PaginationConfiguration {
}

如果我们在 Web 控制器方法中使用或参数而没有 激活了 Spring 数据 Web 支持,我们将得到如下异常:PageableSort

java.lang.NoSuchMethodException: org.springframework.data.domain.Pageable.<init>()
java.lang.NoSuchMethodException: org.springframework.data.domain.Sort.<init>()

这些异常意味着 Spring 尝试创建主动脉实例 并且失败,因为它们没有默认构造函数。PageableSort

Spring Data Web 支持修复了这个问题,因为它将PageableHandlerMethodArgumentResolver 和 SortHandlerMethodArgumentResolverbean 添加到 应用程序上下文,负责查找 Web 控制器方法参数 类型并且页面大小排序查询参数的值填充它们PageableSort

接受参数Pageable

启用 Spring 数据网络支持后,我们可以简单地使用 aas 作为输入参数 到 Web 控制器方法并将 Aobject 返回给客户端:PageablePage

@RestController
@RequiredArgsConstructor
class PagedController {

  private final MovieCharacterRepository characterRepository;

  @GetMapping(path = "/characters/page")
  Page<MovieCharacter> loadCharactersPage(Pageable pageable) {
    return characterRepository.findAllPage(pageable);
  }
  
}

集成测试显示,查询参数,和现在已计算 并“注入”到我们的 Web 控制器方法的参数中:pagesizesortPageable

@WebMvcTest(controllers = PagedController.class)
class PagedControllerTest {

  @MockBean
  private MovieCharacterRepository characterRepository;

  @Autowired
  private MockMvc mockMvc;

  @Test
  void evaluatesPageableParameter() throws Exception {

    mockMvc.perform(get("/characters/page")
        .param("page", "5")
        .param("size", "10")
        .param("sort", "id,desc")   // <-- no space after comma!         .param("sort", "name,asc")) // <-- no space after comma!         .andExpect(status().isOk());

    ArgumentCaptor<Pageable> pageableCaptor = 
        ArgumentCaptor.forClass(Pageable.class);
    verify(characterRepository).findAllPage(pageableCaptor.capture());
    PageRequest pageable = (PageRequest) pageableCaptor.getValue();

    assertThat(pageable).hasPageNumber(5);
    assertThat(pageable).hasPageSize(10);
    assertThat(pageable).hasSort("name", Sort.Direction.ASC);
    assertThat(pageable).hasSort("id", Sort.Direction.DESC);
  }
}

测试捕获传递到存储库方法的参数并验证 它具有查询参数定义的属性。Pageable

请注意,我使用了自定义 AssertJ断言,用于在实例上创建可读断言。Pageable

另请注意,为了按多个字段排序,我们必须提供查询参数 多次。每个字段可以仅包含一个字段名称,假定升序, 或带有顺序的字段名称,用逗号分隔,不带空格。如果有空间 在字段名称和订单之间,不会评估订单sort

接受参数Sort

同样,我们可以在 Web 控制器方法中使用独立参数:Sort

@RestController
@RequiredArgsConstructor
class PagedController {

  private final MovieCharacterRepository characterRepository;

  @GetMapping(path = "/characters/sorted")
  List<MovieCharacter> loadCharactersSorted(Sort sort) {
    return characterRepository.findAllSorted(sort);
  }
}

当然,对象仅使用查询参数的值填充, 如此测试所示:Sortsort

@WebMvcTest(controllers = PagedController.class)
class PagedControllerTest {

  @MockBean
  private MovieCharacterRepository characterRepository;

  @Autowired
  private MockMvc mockMvc;

  @Test
  void evaluatesSortParameter() throws Exception {

    mockMvc.perform(get("/characters/sorted")
        .param("sort",desc")   // <-- no space after comma!!!         .param("sort",asc")) // <-- no space after comma!!!         .andExpect(status().isOk());

    ArgumentCaptor<Sort> sortCaptor = ArgumentCaptor.forClass(Sort.class);
    verify(characterRepository).findAllSorted(sortCaptor.capture());
    Sort sort = sortCaptor.getValue();

    assertThat(sort).hasSort("name", Sort.Direction.ASC);
    assertThat(sort).hasSort("id", Sort.Direction.DESC);
  }
}

自定义全局分页默认值

如果我们在调用 带有参数的控制器方法,它将填充 默认值。pagesizesortPageable

Spring Boot 使用@ConfigurationProperties功能来 将以下属性绑定到类型的 Bean:SpringDataWebProperties

spring.data.web.pageable.size-parameter=size
spring.data.web.pageable.page-parameter=page
spring.data.web.pageable.default-page-size=20
spring.data.web.pageable.one-indexed-parameters=false
spring.data.web.pageable.max-page-size=2000
spring.data.web.pageable.prefix=
spring.data.web.pageable.qualifier-delimiter=_

以上值是默认值。 其中一些属性不是不言自明的,因此以下是它们的作用:

  • 我们可以更改查询参数的名称size-parametersize
  • 我们可以更改查询参数的名称page-parameterpage
  • 如果没有给出值,我们可以定义参数的默认值default-page-sizesize
  • 我们可以选择参数是以 0 开头还是以 1 开头one-indexed-parameterspage
  • 我们可以选择查询参数允许的最大值(大于此值的值将减少)max-page-sizesize
  • 我们可以为AndQuery参数名称定义一个前缀(而不是参数!prefixpagesizesort

物业是一个非常特殊的情况。我们可以在 aMethod 参数,用于为分页查询参数提供本地前缀:qualifier-delimiter@QualifierPageable

@RestController
class PagedController {

  @GetMapping(path = "/characters/qualifier")
  Page<MovieCharacter> loadCharactersPageWithQualifier(
      @Qualifier("my") Pageable pageable) {
    ...
  }

}

这与上面的属性具有类似的效果,但它也适用于参数。用于分隔前缀 参数名称。在上面的示例中,仅计算查询参数。prefixsortqualifier-delimitermy_pagemy_sizemy_sort

spring.data.web.*属性未评估?

如果对上述配置属性的更改不起作用,则 bean 可能未加载到应用程序上下文中。SpringDataWebProperties

其中一个原因可能是您已经激活了分页支持。这将覆盖, 其中创造了沂豆。仅在普通弹簧应用中使用。@EnableSpringDataWebSupportSpringDataWebAutoConfigurationSpringDataWebProperties@EnableSpringDataWebSupport

自定义本地寻呼默认值

有时我们可能只想为单个控制器方法定义默认分页参数。 对于这种情况,我们可以使用theandannotations:@PagableDefault@SortDefault

@RestController
class PagedController {

  @GetMapping(path = "/characters/page")
  Page<MovieCharacter> loadCharactersPage(
      @PageableDefault(page = 0, size = 20)
      @SortDefault.SortDefaults({
          @SortDefault(sort = "name", direction = Sort.Direction.DESC),
          @SortDefault(sort = "id", direction = Sort.Direction.ASC)
      }) Pageable pageable) {
    ...
  }
  
}

如果未给出查询参数,则对象现在将填充 批注中定义的默认值。Pageable

请注意,theannotation 也有属性,但如果我们想要 要定义多个字段以按不同方向排序,我们必须使用。@PageableDefaultsort@SortDefault

Spring 数据存储库中的分页

由于本文中描述的分页功能来自Spring Data, Spring Data完全支持分页也就不足为奇了。然而,这种支持是 解释得非常快,因为我们只需要添加正确的参数并返回值 到我们的存储库界面。

传递分页参数

我们可以简单地将主动脉实例传递到任何 Spring 数据存储库方法中:PageableSort

interface MovieCharacterRepository 
        extends CrudRepository<MovieCharacter, Long> {

  List<MovieCharacter> findByMovie(String movieName, Pageable pageable);
  
  @Query("select c from MovieCharacter c where c.movie = :movie")
  List<MovieCharacter> findByMovieCustom(
      @Param("movie") String movieName, Pageable pageable);
  
  @Query("select c from MovieCharacter c where c.movie = :movie")
  List<MovieCharacter> findByMovieSorted(
      @Param("movie") String movieName, Sort sort);

}

即使Spring Data提供了一个PagingAndSortingRepository,我们也不必 使用它来获取分页支持。它只是 提供两种方便的方法,一种使用 a,另一种使用 a参数。findAllSortPageable

返回页面元数据

如果我们想将页面信息返回给客户端而不是简单的列表, 我们只是让我们的存储库方法简单地返回 aor a:SlicePage

interface MovieCharacterRepository 
        extends CrudRepository<MovieCharacter, Long> {

  Page<MovieCharacter> findByMovie(String movieName, Pageable pageable);

  @Query("select c from MovieCharacter c where c.movie = :movie")
  Slice<MovieCharacter> findByMovieCustom(
      @Param("movie") String movieName, Pageable pageable);

}

每个返回主动脉的方法都必须只有一个参数,否则 Spring Data将在启动时投诉,但有例外。SlicePagePageable

结论

Spring 数据 Web 支持使普通 Spring 应用程序和 Spring 中的分页变得容易。 启动应用程序。这是激活它然后使用正确的输入和输出的问题 控制器和存储库方法中的参数。

借助 Spring Boot 的配置属性,我们可以对默认值进行细粒度控制。 和参数名称。

不过,有一些潜在的问题,其中一些我在上面的文字中已经描述了,所以 你不必绊倒它们。

如果您缺少有关与 Spring 进行寻呼的任何内容,请在此 教程,在评论中让我知道。

您可以在github 上找到本文中使用的示例代码。

原文地址:https://blog.csdn.net/allway2

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


这篇文章主要介绍了spring的事务传播属性REQUIRED_NESTED的原理介绍,具有一定借鉴价值,需要的朋友可以参考下。下面就和我一起来看看吧。传统事务中回滚点的使...
今天小编给大家分享的是一文解析spring中事务的传播机制,相信很多人都不太了解,为了让大家更加了解,所以给大家总结了以下内容,一起往下看吧。一定会有所收获...
这篇文章主要介绍了SpringCloudAlibaba和SpringCloud有什么区别,具有一定借鉴价值,需要的朋友可以参考下。下面就和我一起来看看吧。Spring Cloud Netfli...
本篇文章和大家了解一下SpringCloud整合XXL-Job的几个步骤。有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。第一步:整合pom文件,在S...
本篇文章和大家了解一下Spring延迟初始化会遇到什么问题。有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。List 坑列表 = new ArrayList(2);...
这篇文章主要介绍了怎么使用Spring提供的不同缓存注解实现缓存的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇...
本篇内容主要讲解“Spring中的@Autowired和@Resource注解怎么使用”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学...
今天小编给大家分享一下SpringSecurity怎么定义多个过滤器链的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家
这篇文章主要介绍“Spring的@Conditional注解怎么使用”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Spring的@Con...
这篇文章主要介绍了SpringCloudGateway的熔断限流怎么配置的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇SpringCloud&nb...
今天小编给大家分享一下怎么使用Spring解决循环依赖问题的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考
这篇文章主要介绍“Spring事务及传播机制的原理及应用方法是什么”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Sp...
这篇“SpringCloudAlibaba框架实例应用分析”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价
本篇内容主要讲解“SpringBoot中怎么使用SpringMVC”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习...
这篇文章主要介绍“SpringMVC适配器模式作用范围是什么”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“SpringMVC
这篇“导入SpringCloud依赖失败如何解决”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家...
这篇文章主要讲解了“SpringMVC核心DispatcherServlet处理流程是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来
今天小编给大家分享一下SpringMVCHttpMessageConverter消息转换器怎么使用的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以...
这篇文章主要介绍“Spring框架实现依赖注入的原理是什么”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Spring框架...
本篇内容介绍了“Spring单元测试控制Bean注入的方法是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下