在本文中,介绍PagingAndSortingRepository一个实际示例, 用来实现Spring Data JPA 的接口实现分页和排序
什么是PagingAndSortingRepository?
检索数据的传统方法通常涉及大型列表。这可能会导致性能问题,尤其是在客户端渲染上。分页和排序可以解决这个问题,带来以下几个好处:
- 改进的性能: 通过以较小的块(页面)获取数据,您可以减少带宽使用并缩短加载时间。
- 增强的用户体验: 用户可以使用上一个/下一个按钮或页码有效地浏览数据。
- 灵活排序: 允许用户根据自己的喜好对数据进行排序,从而带来更加个性化的体验。
依赖设置
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.1.8</version> <relativePath/> </parent> <groupId>com.jcg</groupId> <artifactId>paginationsortingexample</artifactId> <version>0.0.1-SNAPSHOT</version> <name>paginationsortingexample</name> <description>Demo project for Spring Boot Pagination and Sorting</description> <properties> <java.version>17</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> </project>
|
数据库配置(application.properties):
spring.datasource.url=jdbc:h2:mem:testdb spring.datasource.driverClassName=org.h2.Driver spring.jpa.hibernate.ddl-auto=update spring.datasource.username=sa spring.datasource.password=password spring.jpa.database-platform=org.hibernate.dialect.H2Dialect spring.h2.console.enabled=true
|
通过这些更新,该项目被配置为使用 H2 内存数据库。http://localhost:8080/h2-console当应用程序运行时,我们可以访问H2控制台。
JPA实体
首先,让我们定义一个实体类来表示我们要使用的数据。对于此示例,我们考虑一个包含、和字段的简单Book实体。
@Entity public class Book { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String title; private String author; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getAuthor() { return author; } public void setAuthor(String author) { this.author = author; } }
|
创建存储库接口
我们将通过扩展Spring Data JPA 提供的接口来创建存储库接口。PagingAndSortingRepository接口提供了两个关键方法:
- findAll(Sort sort): 返回根据给定 Sort 对象排序的所有实体。
- findAll(Pageable pageable): 返回一个 Page 对象,其中包含该对象定义的特定数据页 Pageable 。
我们还可以创建将Pageable或Sort对象作为参数的自定义方法,以满足我们的特定需求。@Repository public interface BookRepository extends PagingAndSortingRepository<Book, Long> { List<Book> findByTitleContaining(String title, Pageable pageable); }
|
我们添加了一个名为 findByTitleContaining(String title, Pageable pageable) 的自定义方法,用于按标题查找图书并支持分页。Pageable pageable 允许进行分页配置,如指定页码、大小、当前页、排序等。
请注意,从 Spring Data 3.0 开始,PagingAndSortingRepository 不再扩展 CrudRepository,因此如果我们想在存储库类中添加 CRUD 功能,就需要让我们的存储库 Bean 扩展 JpaRepository 接口。或者,我们可以显式地从 CrudRepository 或 ListCrudRepository 接口扩展,这样我们就能在执行 CRUD 的同时进行分页和排序。
@Repository public interface BookRepository extends JpaRepository<Book, Long> { } @Repository public interface BookRepository<Book, Long> extends PagingAndSortingRepository<Book, Long>, ListCrudRepository<Book, Long> { }
|
服务层
接下来,让我们实现一个服务层,以便与书库交互并演示分页和排序。在这里,我们注入 BookRepository 并定义一个方法来检索分页和排序的图书。
@Service public class BookService { @Autowired BookRepository bookRepository; public Page<Book> getAllBooks(Pageable pageable) { return bookRepository.findAll(pageable); } public List<Book> searchBooksByTitle(String title, Pageable pageable) { return bookRepository.findByTitleContaining(title, pageable); } public Page<Book> findAllPaginatedAndSorted(int pageNo, int pageSize, String sortBy, String sortDirection) { Sort sort = Sort.by(Sort.Direction.fromString(sortDirection), sortBy); Pageable pageable = PageRequest.of(pageNo, pageSize, sort); return bookRepository.findAll(pageable); } }
|
上述代码:
- public List<Book> searchBooksByTitle(String title, Pageable pageable) 方法根据包含特定子字符串的标题搜索图书,并根据提供的 Pageable 配置进行分页。它将调用委托给注入的 BookRepository 的 findByTitleContaining() 方法。
- public Page<Book> findAllPaginatedAndSorted(int pageNo, int pageSize, String sortBy, String sortDirection) 方法从数据库中检索所有书籍,并根据提供的参数进行分页和排序。该方法根据排序方向和排序字段构建一个 Sort 对象,使用提供的分页参数创建一个 Pageable 对象,然后将调用委托给注入的 BookRepository 的 findAll() 方法。
在控制器中使用服务
最后,让我们创建一个 REST 控制器来公开我们的服务,并测试分页和排序功能。
@RestController @RequestMapping("/api/books") public class BookController { @Autowired BookService bookService; @GetMapping public List<Book> findAllPaginatedAndSorted( @RequestParam(defaultValue = "0") int pageNo, @RequestParam(defaultValue = "5") int pageSize, @RequestParam(defaultValue = "id") String sortBy, @RequestParam(defaultValue = "DESC") String sortDirection) { Page result = bookService.findAllPaginatedAndSorted(pageNo, pageSize, sortBy, sortDirection); return result.toList(); } @GetMapping("/search") public List<Book> searchBooksByTitle( @RequestParam String title, @RequestParam(defaultValue = "0") int pageNo, @RequestParam(defaultValue = "5") int pageSize, @RequestParam(defaultValue = "title") String sortBy) { Pageable pageable = PageRequest.of(pageNo, pageSize, Sort.by(sortBy)); return bookService.searchBooksByTitle(title, pageable); } }
|
图书控制器(BookController)提供了两个与图书交互的端点:一个用于检索所有图书的分页和排序列表,另一个用于按标题搜索图书,并进行分页和可选排序。
- public List<Book> findAllPaginatedAndSorted(...)方法:可检索已分页和排序的图书列表。该方法从 URL 的查询字符串中获取分页(pageNo、pageSize)和排序(sortBy、sortDirection)参数。它将调用委托给注入的 BookService 的 findAllPaginatedAndSorted 方法。
- public List<Book> searchBooksByTitle(...)方法:根据包含特定子字符串的书名搜索图书。它从 URL 的查询字符串中获取要搜索的标题(title)、分页(pageNo、pageSize)和排序(sortBy)参数。它根据分页和排序参数构建一个 Pageable 对象,然后将调用委托给注入的 BookService 的 searchBooksByTitle 方法。
测试应用程序
要在 H2 数据库中创建一个 SQL 表并加载一些示例值,我们可以在应用程序初始化过程中以编程方式执行 SQL 脚本,我们可以利用 schema.sql 和 data.sql 文件:
在 src/main/resources 文件夹中创建 schema.sql 文件。该文件包含创建表格的 SQL 命令。
CREATE TABLE IF NOT EXISTS Book ( id INT AUTO_INCREMENT PRIMARY KEY, title VARCHAR(255), author VARCHAR(255) );
|
在 src/main/resources 文件夹中创建 data.sql 文件。该文件包含向表中插入示例数据的 SQL 命令。
INSERT INTO Book (title, author) VALUES ('Core HTML5 Canvas', 'Geary'); INSERT INTO Book (title, author) VALUES ('Java EE 6 Platform with Glassfish 3', 'Goncalves'); INSERT INTO Book (title, author) VALUES ('Core Java Fundamentals', 'Horstmann Cornell'); INSERT INTO Book (title, author) VALUES ('JavaScript and JQuery', 'McFarland'); INSERT INTO Book (title, author) VALUES ('Real World Java EE Patterns', 'A.Bien'); INSERT INTO Book (title, author) VALUES ('Age of Reason', 'T.Paine'); INSERT INTO Book (title, author) VALUES ('Smashing CSS', 'Meyer'); INSERT INTO Book (title, author) VALUES ('The Essential Blender', 'Hess'); INSERT INTO Book (title, author) VALUES ('Pro JavaScript for Web Apps', 'Freeman'); INSERT INTO Book (title, author) VALUES ('Java EE 7 Essentials', 'A.Gupta'); INSERT INTO Book (title, author) VALUES ('JavaScript Enlightenment', 'Lindley');
|
更新 application.properties 文件。在 application.properties 文件中,我们需要指定以下属性,以告诉 Spring Boot 在应用程序启动时执行这些脚本:
application.properties
spring.datasource.initialization-mode=always spring.datasource.schema=classpath:schema.sql spring.datasource.data=classpath:data.sql
|
现在,我们可以运行 Spring Boot 应用程序,并使用查询参数向 /api/books 端点发出 HTTP 请求,以进行分页和排序。
例如,我们可以使用以下请求参数发送 HTTP 请求:PageNo = 0、PageSize = 5、SortBy = author、SortDirection = DESC 并观察输出结果:
http://localhost:8080/api/books?pageNo=0&pageSize=5&sortBy=author&sortDirection=desc
使用以下请求参数在网络浏览器上再次进行测试:PageNo = 1, PageSize = 4, SortBy = title, SortDirection = ASC
http://localhost:8080/api/books?pageNo=1&pageSize=4&sortBy=title&sortDirection=asc
结论
在本文中,我们探讨了如何使用 Spring Data JPA 的 PagingAndSortingRepository 接口在 Spring Boot 应用程序中实现分页和排序。利用这些功能,我们可以高效地管理大型数据集,提高查询性能,并增强应用程序的整体用户体验。