为 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 作为输入参数,因为它提供 要加载的页面编号以及页面大小。通过课堂, 它还允许定义要排序的字段及其方向 应排序(升序或降序)。Pageable
Sort
创建实例的最常见方法是使用实现:Pageable
PageRequest
Pageable pageable = PageRequest.of(0, 5, Sort.by(
Order.asc("name"),
Order.desc("id")));
这将为第一页创建一个请求,其中首先订购了 5 个项目 按名称(升序),按 ID (降序)。请注意,默认情况下页面索引是从零开始的!
困惑?java.awt.print.Pageable
使用时,您会注意到 IDE 有时会 建议进口代替春天的班。由于我们很可能不需要包中的任何类,因此我们可以 告诉我们的 IDE 完全忽略它。Pageable
java.awt.print.Pageable
Pageable
java.awt
在IntelliJ中,转到设置中的“常规->编辑器->自动导入”,然后添加到标有“从导入和完成中排除”的列表中。java.awt.*
在 Eclipse 中,转到首选项中的“Java -> 外观 -> 类型过滤器”,然后 添加到包列表中。java.awt.*
春天的数据沙子Page
Slice
同时捆绑分页请求的输入参数,与接口 为返回给客户端的项目页面提供元数据(评论是我的):Pageable
Page
Slice
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
接口最常见的实现由类提供:Page
PageImpl
Pageable pageable = ...;
List<MovieCharacter> listOfCharacters = ...;
long totalCharacters = 100;
Page<MovieCharacter> page =
new PageImpl<>(listOfCharacters, pageable, totalCharacters);
Web 控制器中的分页
如果我们想在 Web 控制器中返回一个(或)项,它需要接受 定义分页参数的参数,将其传递给数据库, ,然后将 aobject 返回给客户端。Page
Slice
Pageable
Page
激活弹簧数据网络支持
分页必须由底层持久性层支持,以便 为任何查询提供分页答案。这就是为什么分页
和页面
类源自 Spring 数据模块,而不是像人们可能的那样。 可疑,来自Spring Web模块。
在启用了自动配置(这是默认设置)的 Spring 启动应用程序中,我们没有 做任何事情,因为它将加载默认情况下,这 包括加载必要 Bean 的注释。SpringDataWebAutoConfiguration
@EnableSpringDataWebSupport
在没有 Spring Boot 的普通 Spring 应用程序中,我们必须自己在类上使用:@EnableSpringDataWebSupport
@Configuration
@Configuration
@EnableSpringDataWebSupport
class PaginationConfiguration {
}
如果我们在 Web 控制器方法中使用或参数而没有 激活了 Spring 数据 Web 支持,我们将得到如下异常:Pageable
Sort
java.lang.NoSuchMethodException: org.springframework.data.domain.Pageable.<init>()
java.lang.NoSuchMethodException: org.springframework.data.domain.Sort.<init>()
这些异常意味着 Spring 尝试创建主动脉实例 并且失败,因为它们没有默认构造函数。Pageable
Sort
Spring Data Web 支持修复了这个问题,因为它将PageableHandlerMethodArgumentResolver 和 SortHandlerMethodArgumentResolverbean 添加到 应用程序上下文,负责查找 Web 控制器方法参数 类型并且用页面
、大小
和排序
查询参数的值填充它们。Pageable
Sort
接受参数Pageable
启用 Spring 数据网络支持后,我们可以简单地使用 aas 作为输入参数 到 Web 控制器方法并将 Aobject 返回给客户端:Pageable
Page
@RestController
@RequiredArgsConstructor
class PagedController {
private final MovieCharacterRepository characterRepository;
@GetMapping(path = "/characters/page")
Page<MovieCharacter> loadCharactersPage(Pageable pageable) {
return characterRepository.findAllPage(pageable);
}
}
集成测试显示,查询参数,和现在已计算 并“注入”到我们的 Web 控制器方法的参数中:page
size
sort
Pageable
@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);
}
}
当然,对象仅使用查询参数的值填充, 如此测试所示:Sort
sort
@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);
}
}
自定义全局分页默认值
如果我们在调用 带有参数的控制器方法,它将填充 默认值。page
size
sort
Pageable
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-parameter
size
- 我们可以更改查询参数的名称
page-parameter
page
- 如果没有给出值,我们可以定义参数的默认值
default-page-size
size
- 我们可以选择参数是以 0 开头还是以 1 开头
one-indexed-parameters
page
- 我们可以选择查询参数允许的最大值(大于此值的值将减少)
max-page-size
size
- 我们可以为AndQuery参数名称定义一个前缀(而不是参数!
prefix
page
size
sort
该物业是一个非常特殊的情况。我们可以在 aMethod 参数,用于为分页查询参数提供本地前缀:qualifier-delimiter
@Qualifier
Pageable
@RestController
class PagedController {
@GetMapping(path = "/characters/qualifier")
Page<MovieCharacter> loadCharactersPageWithQualifier(
@Qualifier("my") Pageable pageable) {
...
}
}
这与上面的属性具有类似的效果,但它也适用于参数。用于分隔前缀 参数名称。在上面的示例中,仅计算查询参数。prefix
sort
qualifier-delimiter
my_page
my_size
my_sort
spring.data.web.*
属性未评估?
如果对上述配置属性的更改不起作用,则 bean 可能未加载到应用程序上下文中。SpringDataWebProperties
其中一个原因可能是您已经激活了分页支持。这将覆盖, 其中创造了沂豆。仅在普通弹簧应用中使用。@EnableSpringDataWebSupport
SpringDataWebAutoConfiguration
SpringDataWebProperties
@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 也有属性,但如果我们想要 要定义多个字段以按不同方向排序,我们必须使用。@PageableDefault
sort
@SortDefault
Spring 数据存储库中的分页
由于本文中描述的分页功能来自Spring Data, Spring Data完全支持分页也就不足为奇了。然而,这种支持是 解释得非常快,因为我们只需要添加正确的参数并返回值 到我们的存储库界面。
传递分页参数
我们可以简单地将主动脉实例传递到任何 Spring 数据存储库方法中:Pageable
Sort
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参数。findAll
Sort
Pageable
返回页面元数据
如果我们想将页面信息返回给客户端而不是简单的列表, 我们只是让我们的存储库方法简单地返回 aor a:Slice
Page
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将在启动时投诉,但有例外。Slice
Page
Pageable
结论
Spring 数据 Web 支持使普通 Spring 应用程序和 Spring 中的分页变得容易。 启动应用程序。这是激活它然后使用正确的输入和输出的问题 控制器和存储库方法中的参数。
借助 Spring Boot 的配置属性,我们可以对默认值进行细粒度控制。 和参数名称。
不过,有一些潜在的问题,其中一些我在上面的文字中已经描述了,所以 你不必绊倒它们。
如果您缺少有关与 Spring 进行寻呼的任何内容,请在此 教程,在评论中让我知道。
您可以在github 上找到本文中使用的示例代码。
原文地址:https://blog.csdn.net/allway2
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。