在前面的教程中,我们使用Spring Boot和 Thymeleaf 开发了一个 CRUD Web 应用程序。
在本教程中,我们将扩展spring boot CRUD web 应用程序,并使用 spring boot、thymeleaf、spring data JPA、Hibernate 和 MySQL 数据库实现分页和排序操作。如您所知,分页允许用户一次查看一小部分数据(一页),排序允许用户以更有条理的方式查看数据。分页和排序都可以帮助用户更轻松、更方便地消费信息。
让我们从可以从本教程下载的员工管理系统项目开始,该项目基于 Spring Boot、Spring Data JPA、Hibernate、Thymeleaf 和 MySQL 数据库。
先决条件
带有 Thymeleaf、Spring MVC、Spring Data JPA、Hibernate、MySQL 的 Spring Boot CRUD Web 应用程序
项目要求
这是实现员工列表页面(员工管理系统) 的分页和排序的高级项目需求。
用户应该能够:
- 在员工列表页面上执行分页
- 在员工列表页面上进行排序
我们将建造什么?
我们将扩展spring boot CRUD web 应用程序,并使用 spring boot、thymeleaf、spring data JPA、Hibernate 和 MySQL 数据库实现分页和排序。
下面的截图总结了我们将在本教程中开发的分页和排序操作。
分页:
- 在员工列表页面上执行分页
- 在员工列表页面上进行排序
我们将建造什么?
我们将扩展spring boot CRUD web 应用程序,并使用 spring boot、thymeleaf、spring data JPA、Hibernate 和 MySQL 数据库实现分页和排序。
下面的截图总结了我们将在本教程中开发的分页和排序操作。
分页:
排序:
首先,我们将逐步实现分页操作,然后我们将完成排序操作。
一、分页实现
了解 Spring Data JPA 的分页 API
要使用 Spring Data JPA 提供的分页和排序 API,您的存储库接口必须扩展 PagingAndSortingRepository 接口,该接口定义了以下几个方法(T 指的是实体类):
@NoRepositoryBean
public interface PagingAndSortingRepository < T, ID > extends CrudRepository < T, ID > {
Iterable < T > findAll(Sort sort);
Page < T > findAll(Pageable pageable);
}
JpaRepository 接口扩展 了 PagingAndSortingRepository 接口,因此如果您的存储库接口的类型为 JpaRepository,则无需对其进行更改。
例如,使用以下命令从数据库中获取第一页,每页 5 个项目:
了解 Spring Data JPA 的分页 API
要使用 Spring Data JPA 提供的分页和排序 API,您的存储库接口必须扩展 PagingAndSortingRepository 接口,该接口定义了以下几个方法(T 指的是实体类):
@NoRepositoryBean
public interface PagingAndSortingRepository < T, ID > extends CrudRepository < T, ID > {
Iterable < T > findAll(Sort sort);
Page < T > findAll(Pageable pageable);
}
int pageNumber = 1; int pageSize = 5; Pageable pageable = PageRequest.of(pageNumber, pageSize); Page然后就可以得到实际内容如下:page = repository.findAll(pageable);
List使用 Page 对象,您可以根据给定的页面大小了解数据库中的总行数和总页数:listEmployees = page.getContent();
long totalItems = page.getTotalElements(); int totalPages = page.getTotalPages();此信息对于使用 Thymeleaf 模板在视图中实现分页很有用。
分页的后端更改
EmployeeService.java 接口更改
在此接口中添加以下方法:
Page findPaginated(int pageNo, int pageSize);
完整代码:
PagefindPaginated(int pageNo, int pageSize);
package net.javaguides.springboot.service;
import java.util.List;
import org.springframework.data.domain.Page;
import net.javaguides.springboot.model.Employee;
public interface EmployeeService {
List < Employee > getAllEmployees();
void saveEmployee(Employee employee);
Employee getEmployeeById(long id);
void deleteEmployeeById(long id);
Page < Employee > findPaginated(int pageNo, int pageSize);
}
EmployeeServiceImpl.java 类更改
将以下方法添加到 EmployeeServiceImpl 类:
@Override
public Page findPaginated(int pageNo, int pageSize) {
Pageable pageable = PageRequest.of(pageNo - 1, pageSize);
return this.employeeRepository.findAll(pageable);
}
EmployeeController.java 类更改
将以下处理程序方法添加到 EmployeeController 类以执行分页:
@GetMapping("/page/{pageNo}")
public String findPaginated(@PathVariable(value = "pageNo") int pageNo, Model model) {
int pageSize = 5;
Page < Employee > page = employeeService.findPaginated(pageNo, pageSize);
List < Employee > listEmployees = page.getContent();
model.addAttribute("currentPage", pageNo);
model.addAttribute("totalPages", page.getTotalPages());
model.addAttribute("totalItems", page.getTotalElements());
model.addAttribute("listEmployees", listEmployees);
return "index";
}
此外,我们需要对现有方法进行更改,如下所示:
// display list of employees
@GetMapping("/")
public String viewHomePage(Model model) {
return findPaginated(1, model);
}
分页的前端更改
将以下分页代码添加到 index.html:
1}">
Total Rows: [[${totalItems}]]
[[${i}]]
[[${i}]]
Next
Next
Last
Last
完整代码:
Employee Management System
Employees List
Add Employee
| Employee First Name | Employee Last Name | Employee Email | Actions |
|---|---|---|---|
| Update Delete |
2.排序实现
了解 Spring Data JPA 的排序 API
Spring Data JPA 提供了 PagingAndSortingRepository 接口,该接口支持使用以下 API 进行排序和分页:
@NoRepositoryBean
public interface PagingAndSortingRepository < T, ID > extends CrudRepository < T, ID > {
Iterable < T > findAll(Sort sort);
Page < T > findAll(Pageable pageable);
}
用户将能够通过单击表格的列标题对员工列表进行排序。
首先,像这样创建一个 Sort 对象:
@NoRepositoryBean
public interface PagingAndSortingRepository < T, ID > extends CrudRepository < T, ID > {
Iterable < T > findAll(Sort sort);
Page < T > findAll(Pageable pageable);
}
Sort sort = Sort.by(“fieldName”).ascending();这将按 fieldName 升序对结果进行排序。 fieldName 必须与实体类中声明的字段名称匹配。 我们还可以按多个字段排序,例如:
Sort sort = Sort.by("fieldName1").ascending().and(Sort.by("fieldName2").descending());
然后我们通过 Sort 对象来创建一个 Pageable ,如下所示:
Pageable pageable = PageRequest.of(pageNum - 1, pageSize, sort);让我们在示例部分了解更多。
用于排序的后端更改
EmployeeService.java 接口更改
让我们在现有方法中添加两个字段:
Page findPaginated(int pageNo, int pageSize, String sortField, String sortDirection);
完整代码:
PagefindPaginated(int pageNo, int pageSize, String sortField, String sortDirection);
package net.javaguides.springboot.service;
import java.util.List;
import org.springframework.data.domain.Page;
import net.javaguides.springboot.model.Employee;
public interface EmployeeService {
List < Employee > getAllEmployees();
void saveEmployee(Employee employee);
Employee getEmployeeById(long id);
void deleteEmployeeById(long id);
Page < Employee > findPaginated(int pageNo, int pageSize, String sortField, String sortDirection);
}
EmployeeServiceImpl.java 类更改
以下方法中实现的排序逻辑:
@Override
public Page findPaginated(int pageNo, int pageSize, String sortField, String sortDirection) {
Sort sort = sortDirection.equalsIgnoreCase(Sort.Direction.ASC.name()) ? Sort.by(sortField).ascending() :
Sort.by(sortField).descending();
Pageable pageable = PageRequest.of(pageNo - 1, pageSize, sort);
return this.employeeRepository.findAll(pageable);
}
完整代码:
package net.javaguides.springboot.service;
import java.util.List;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import net.javaguides.springboot.model.Employee;
import net.javaguides.springboot.repository.EmployeeRepository;
@Service
public class EmployeeServiceImpl implements EmployeeService {
@Autowired
private EmployeeRepository employeeRepository;
@Override
public List < Employee > getAllEmployees() {
return employeeRepository.findAll();
}
@Override
public void saveEmployee(Employee employee) {
this.employeeRepository.save(employee);
}
@Override
public Employee getEmployeeById(long id) {
Optional < Employee > optional = employeeRepository.findById(id);
Employee employee = null;
if (optional.isPresent()) {
employee = optional.get();
} else {
throw new RuntimeException(" Employee not found for id :: " + id);
}
return employee;
}
@Override
public void deleteEmployeeById(long id) {
this.employeeRepository.deleteById(id);
}
@Override
public Page < Employee > findPaginated(int pageNo, int pageSize, String sortField, String sortDirection) {
Sort sort = sortDirection.equalsIgnoreCase(Sort.Direction.ASC.name()) ? Sort.by(sortField).ascending() :
Sort.by(sortField).descending();
Pageable pageable = PageRequest.of(pageNo - 1, pageSize, sort);
return this.employeeRepository.findAll(pageable);
}
}
EmployeeController.java 更改
让我们更改现有方法以提供对排序的支持:
@GetMapping("/page/{pageNo}")
public String findPaginated(@PathVariable(value = "pageNo") int pageNo,
@RequestParam("sortField") String sortField,
@RequestParam("sortDir") String sortDir,
Model model) {
int pageSize = 5;
Page < Employee > page = employeeService.findPaginated(pageNo, pageSize, sortField, sortDir);
List < Employee > listEmployees = page.getContent();
model.addAttribute("currentPage", pageNo);
model.addAttribute("totalPages", page.getTotalPages());
model.addAttribute("totalItems", page.getTotalElements());
model.addAttribute("sortField", sortField);
model.addAttribute("sortDir", sortDir);
model.addAttribute("reverseSortDir", sortDir.equals("asc") ? "desc" : "asc");
model.addAttribute("listEmployees", listEmployees);
return "index";
}
还为主页提供默认的排序字段和排序方向:
// display list of employees
@GetMapping("/")
public String viewHomePage(Model model) {
return findPaginated(1, "firstName", "asc", model);
}
完整代码:
package net.javaguides.springboot.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import net.javaguides.springboot.model.Employee;
import net.javaguides.springboot.service.EmployeeService;
@Controller
public class EmployeeController {
@Autowired
private EmployeeService employeeService;
// display list of employees
@GetMapping("/")
public String viewHomePage(Model model) {
return findPaginated(1, "firstName", "asc", model);
}
@GetMapping("/showNewEmployeeForm")
public String showNewEmployeeForm(Model model) {
// create model attribute to bind form data
Employee employee = new Employee();
model.addAttribute("employee", employee);
return "new_employee";
}
@PostMapping("/saveEmployee")
public String saveEmployee(@ModelAttribute("employee") Employee employee) {
// save employee to database
employeeService.saveEmployee(employee);
return "redirect:/";
}
@GetMapping("/showFormForUpdate/{id}")
public String showFormForUpdate(@PathVariable(value = "id") long id, Model model) {
// get employee from the service
Employee employee = employeeService.getEmployeeById(id);
// set employee as a model attribute to pre-populate the form
model.addAttribute("employee", employee);
return "update_employee";
}
@GetMapping("/deleteEmployee/{id}")
public String deleteEmployee(@PathVariable(value = "id") long id) {
// call delete employee method
this.employeeService.deleteEmployeeById(id);
return "redirect:/";
}
@GetMapping("/page/{pageNo}")
public String findPaginated(@PathVariable(value = "pageNo") int pageNo,
@RequestParam("sortField") String sortField,
@RequestParam("sortDir") String sortDir,
Model model) {
int pageSize = 5;
Page < Employee > page = employeeService.findPaginated(pageNo, pageSize, sortField, sortDir);
List < Employee > listEmployees = page.getContent();
model.addAttribute("currentPage", pageNo);
model.addAttribute("totalPages", page.getTotalPages());
model.addAttribute("totalItems", page.getTotalElements());
model.addAttribute("sortField", sortField);
model.addAttribute("sortDir", sortDir);
model.addAttribute("reverseSortDir", sortDir.equals("asc") ? "desc" : "asc");
model.addAttribute("listEmployees", listEmployees);
return "index";
}
}
排序的前端更改
索引.html
我们通过使用以下代码添加超链接来使表格的标题列可排序:
Employee First Name
Employee Last Name
Employee Email
Actions
我们还需要更改分页部分以提供排序支持,例如:
Employee First Name Employee Last Name Employee Email Actions
1}">
Total Rows: [[${totalItems}]]
[[${i}]]
[[${i}]]
Next
Next
Last
Last
完整代码:
Employee Management System Employees List Add Employee
| Employee First Name | Employee Last Name | Employee Email | Actions |
|---|---|---|---|
| Update Delete |



