在前面的教程中,我们已经看到了如何使用 Spring Data 分页 Web 支持。在本教程中,我们将了解如何使用排序支持。
在基于 Web 的应用程序中应用排序主要有两种方法:
按可分页处理程序参数排序
我们在前面的示例中使用过,但没有任何排序查询参数。除了分页信息外,还可以设置排序信息(查看这个和这个)。在这种情况下,我们必须使用类似于以下内容的查询字符串:Pageable
Pageable
?page=1&size=10&sort=personName,desc
相当于:
PageRequest.of(1,10, Sort.by("personName").descending())
按排序处理程序参数排序
我们也可以用作控制器方法参数(查看我们的排序基本示例)。在这种情况下,我们需要创建类似于以下内容的查询字符串:Sort
?sort=personName,desc
相当于:
Sort.by("personName").descending()
@SortDefault注释
此批注定义将 Sort 实例注入控制器处理程序方法时要使用的默认排序选项。
SortHandlerMethodArgumentResolver
在上述两种情况下,用于从请求参数或 SortDefault 注释自动创建排序实例。当我们在配置类中使用时,此解析器处于活动状态。SortHandlerMethodArgumentResolver
@EnableSpringDataWebSupport
例
实体
@Entity
public class Employee {
private @Id
@GeneratedValue
Long id;
private String name;
private String dept;
private int salary;
.............
}
存储 库
public interface EmployeeRepository extends PagingAndSortingRepository<Employee, Long> {
@Query("SELECT DISTINCT e.dept FROM Employee e")
List<String> findAllDepartments(Sort sort);
}
我们在上面的存储库 withparameter 中创建了一个自定义查询方法,以便我们可以在控制器中使用 Sort 参数,该参数只会为视图生成部门列表。Sort
MVC 控制器
在下面的控制器中,我们将使用两种处理程序方法,一种是使用可分页参数,另一种是使用 Sort 参数:
@Controller
public class EmployeeController {
@Autowired
private EmployeeRepository repository;
@GetMapping("/employees")
public String getEmployees(@PageableDefault(size = 10, sort = "id") Pageable pageable,
Model model) {
Page<Employee> page = repository.findAll(pageable);
List<Sort.Order> sortOrders = page.getSort().stream().collect(Collectors.toList());
if (sortOrders.size() > 0) {
Sort.Order order = sortOrders.get(0);
model.addAttribute("sortProperty", order.getProperty());
model.addAttribute("sortDesc", order.getDirection() == Sort.Direction.DESC);
}
model.addAttribute("page", page);
return "employee-page";
}
@GetMapping("departments")
public String getDepartments(@SortDefault(sort="dept",direction = Sort.Direction.ASC)
Sort sort, Model model) {
List<String> depts = repository.findAllDepartments(sort);
model.addAttribute("depts", depts);
return "dept-page";
}
}
Thymeleaf 视图
跟随视图同时实现排序和分页。我们正在通过单击表标题进行单列排序。这类似于Java Swing JTable排序功能(在此处查看示例)。
我们使用 JQuery 来处理表头单击和显示箭头;▾ 和 ▴ 分别在正确的位置进行降序和升序排序。
src/main/webapp/WEB-INF/views/employee-page.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script th:inline="javascript">
//thymeleaf to javascript variables
/*<![CDATA[*/
var sortProperty = /*[[${sortProperty}]]*/ null;
var sortDesc = /*[[${sortDesc}]]*/ null;
var currentPage = /*[[${page.number}]]*/ 0;
var pageSize =/*[[${page.size}]]*/ 0;
/*]]>*/
$(document).ready(function(){
//show up/down arrows
$("table#emp-table thead th").each(function(){
var head = $(this);
if(head.attr('data-sort-prop')==sortProperty){
head.append(sortDesc?'▾':'▴');
}
});
//set click action, reload page on clicking with all query params
$("table#emp-table thead th").click(function() {
var headerSortPropName = $(this).attr("data-sort-prop");
if(headerSortPropName==sortProperty){
window.location.href = window.location.pathname+
'?page='+currentPage+'&size='+pageSize+'&sort='+ headerSortPropName+','+
(sortDesc?'asc':'desc');
}else{
window.location.href = window.location.pathname+
'?page='+currentPage+'&size='+pageSize+'&sort='+ headerSortPropName+',asc';
}
});
});
</script>
<style>
table{width:100%;}
table td, table th { border: 1px solid grey;}
table th { user-select: none; background: #eee;}
table tr th:first-child{width:100px;}
table tr th:nth-child(3){width:150px;}
table tr th:nth-child(4){width:150px;}
.pagination-div{user-select: none;}
.pagination-div span{border-radius:3px;border:1px solid #999;
padding:5px;margin:10px 0px 0px 10px;display:inline-block}
span.selected{background:#ccf;}
</style>
</head>
<body>
<h2>Employees</h2>
<table id="emp-table">
<thead>
<tr>
<th data-sort-prop="id">Id</th>
<th data-sort-prop="name">Name</th>
<th data-sort-prop="dept">Department</th>
<th data-sort-prop="salary">Salary</th>
</tr>
</thead>
<tr th:each="employee : ${page.content}">
<td th:text="${employee.id}"></td>
<td th:text="${employee.name}"></td>
<td th:text="${employee.dept}"></td>
<td th:text="${employee.salary}"></td>
</tr>
</table>
<!-- using th:with to declare a local variable for sorting query param -->
<div class="pagination-div" th:with="sortParam=${sortProperty+','+(sortDesc?'desc':'asc')}">
<span th:if="${page.hasPrevious()}">
<a th:href="@{/employees(page=${page.number-1},size=${page.size},sort=${sortParam})}">Previous</a>
</span>
<th:block th:each="i: ${#numbers.sequence(0, page.totalPages - 1)}">
<span th:if="${page.number == i}" class="selected">[[${i}+1]]</span>
<span th:unless="${page.number == i}">
<a th:href="@{/employees(page=${i},size=${page.size},sort=${sortParam})}">[[${i}+1]]</a>
</span>
</th:block>
<span th:if="${page.hasNext()}">
<a th:href="@{/employees(page=${page.number+1},size=${page.size},sort=${sortParam})}">Next</a>
</span>
</div>
</body>
</html>
以下视图仅显示部门列表。相应的处理程序方法(如上所示)仅使用参数。Sort
src/main/webapp/WEB-INF/views/dept-page.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org">
<body>
<h2>Departments</h2>
<p th:each="dept : ${depts}">
<td th:text="${dept}"></td>
</p>
<br>
<a th:href="@{/departments(sort='dept,asc')}">In ascending order</a> <br/>
<a th:href="@{/departments(sort='dept,desc')}">In descending order</a>
</body>
</html>
运行
要尝试示例,请运行以下示例项目的嵌入式tomcat(在pom中配置.xml):
mvn tomcat7:run-war
输出
localhost:8080/employees
如上所示,默认情况下选择第一页,并按升序对“id”列进行排序。让我们点击“名称”列标题:
查看上面地址栏中的查询字符串,其中存在所有必需的查询参数。单击“名称”标题上的更多时间将更改排序方向:
单击分页按钮可保留上次排序的列。
让我们通过本地主机访问部门页面:localhost:8080/departments
单击底部的“降序”链接将按降序显示部门:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org">
<head>
<link rel="icon" th:href="@{/assets/img/favicon.png}" />
<link rel="stylesheet" type="text/css" th:href="@{/webjars/bootstrap/css/bootstrap.min.css}" />
<link rel="stylesheet" type="text/css" th:href="@{/webjars/font-awesome/css/all.min.css}"/>
<link rel="stylesheet" type="text/css" th:href="@{/webjars/bootstrap-icons/font/bootstrap-icons.css}"/>
<link rel="stylesheet" type="text/css" th:href="@{/assets/css/style.css}" />
<link rel="stylesheet" type="text/css" th:href="@{/css/style.css}" />
<script type="text/javascript" th:src="@{/webjars/jquery/jquery.min.js}"></script>
<script type="text/javascript" th:src="@{/webjars/bootstrap/js/bootstrap.bundle.min.js}"></script>
<script th:inline="javascript">
//thymeleaf to javascript variables
/*<![CDATA[*/
var sortProperty = /*[[${sortProperty}]]*/ null;
var sortDesc = /*[[${sortDesc}]]*/ null;
var currentPage = /*[[${page.number}]]*/ 0;
var pageSize = /*[[${page.size}]]*/ 0;
/*]]>*/
$(document).ready(function () {
//show up/down arrows
$("table#emp-table thead th").each(function () {
var head = $(this);
if (head.attr('data-sort-prop') == sortProperty) {
head.append(sortDesc ? '▾' : '▴');
}
});
//set click action, reload page on clicking with all query params
$("table#emp-table thead th").click(function () {
var headerSortPropName = $(this).attr("data-sort-prop");
if (headerSortPropName == sortProperty) {
window.location.href = window.location.pathname +
'?page=' + currentPage + '&size=' + pageSize + '&sort=' + headerSortPropName + ',' +
(sortDesc ? 'asc' : 'desc');
} else {
window.location.href = window.location.pathname +
'?page=' + currentPage + '&size=' + pageSize + '&sort=' + headerSortPropName + ',asc';
}
});
});
</script>
<style>
table{
width:100%;
}
table td, table th {
border: 1px solid grey;
}
table th {
user-select: none;
background: #eee;
}
table tr th:first-child{
width:100px;
}
table tr th:nth-child(3){
width:150px;
}
table tr th:nth-child(4){
width:150px;
}
.pagination-div{
user-select: none;
}
.pagination-div span{
border-radius:3px;
border:1px solid #999;
padding:5px;
margin:10px 0px 0px 10px;
display:inline-block
}
span.selected{
background:#ccf;
}
</style>
</head>
<body>
<h2>Employees</h2>
<div class="my-3 ">
<div class="row d-flex flex-row">
<form th:action="@{/employees}" id="searchForm" >
<div class="row">
<div class="col-sm-8">
<div class="col-md-12 mt-2">
<div class="search">
<i class="fa fa-search"></i>
<input id="keyword" type="search" name="keyword" th:value="${keyword}" required class="form-control"
placeholder="Enter keyword">
<button type="submit" class="btn btn-secondary">搜索</button>
</div>
</div>
</div>
<div class="col-sm-3">
<div class="col-md-12 input-group mt-2">
<div class="input-group-prepend">
<label class="input-group-text" for="pageSize">每页显示行数:</label>
</div>
<select form="searchForm" name="size" th:value="${pageSize}" onchange="changePageSize()" class="size-select"
id="pageSize">
<option th:each="s : ${ {10, 25, 50} }" th:value="${s}" th:text="${s}" th:selected="${s == page.size}"></option>
</select>
</div>
</div>
<div class="col-sm-1">
<div class="col-md-12 mt-2">
<button id="btnClear" class="btn btn-info">重置</button>
</div>
</div>
</div>
</form>
</div>
</div>
<table id="emp-table">
<thead>
<tr>
<th data-sort-prop="id">Id</th>
<th data-sort-prop="name">Name</th>
<th data-sort-prop="dept">Department</th>
<th data-sort-prop="salary">Salary</th>
</tr>
</thead>
<tr th:each="employee : ${page.content}">
<td th:text="${employee.id}"></td>
<td th:text="${employee.name}"></td>
<td th:text="${employee.dept}"></td>
<td th:text="${employee.salary}"></td>
</tr>
</table>
<!-- using th:with to declare a local variable for sorting query param -->
<div class="pagination-div" th:with="sortParam=${sortProperty+','+(sortDesc?'desc':'asc')}">
<span th:if="${page.hasPrevious()}">
<a th:href="@{/employees(page=${page.number-1},size=${page.size},sort=${sortParam})}">Previous</a>
</span>
<th:block th:each="i: ${#numbers.sequence(0, page.totalPages - 1)}">
<span th:if="${page.number == i}" class="selected">[[${i}+1]]</span>
<span th:unless="${page.number == i}">
<a th:href="@{/employees(page=${i},size=${page.size},sort=${sortParam})}">[[${i}+1]]</a>
</span>
</th:block>
<span th:if="${page.hasNext()}">
<a th:href="@{/employees(page=${page.number+1},size=${page.size},sort=${sortParam})}">Next</a>
</span>
</div>
<div class="row">
<div class="col-sm-6">
<div th:if="${page.totalPages > 0}">
总计<span th:text="${page.totalElements}">99</span>行,当前显示<span th:text="(${page.number} )*${page.size} +1">1</span> -
<span th:text="(${page.number} )*${page.size} +${page.numberOfElements}">5</span>行 总计<span th:text="${page.totalPages}">99</span>页
</div>
</div>
<div class="col-sm-6" th:with="sortParam=${sortProperty+','+(sortDesc?'desc':'asc')}">
<nav aria-label="Pagination" th:if="${page.totalPages > 0}">
<ul class="pagination justify-content-center">
<li class="page-item" th:classappend="${page.number == 0} ? 'disabled'">
<a class="page-link"
th:href="@{'/employees?' + ${keyword!=null && keyword!=''? 'keyword=' + keyword + '&' : ''} + 'page=' +'0'+ '&size=' + ${page.size}+'&sort='+${sortParam}}"
title="First Page" rel="First Page">
最前页
</a>
</li>
<li class="page-item font-weight-bold" th:classappend="${page.number == 0} ? 'disabled'">
<a class="page-link"
th:href="@{'/employees?' + ${keyword!=null && keyword!=''? 'keyword=' + keyword + '&' : ''} + 'page=' + ${page.number - 1} + '&size=' + ${page.size}+'&sort='+${sortParam}}"
title="Previous Page" rel="Previous Page">
前一页
</a>
</li>
<li class="page-item disabled" th:if="${page.number - 2 > 0}">
<a class="page-link" href="#">...</a>
</li>
<li
th:each="i : ${#numbers.sequence(page.number > 2 ? page.number - 2 : 0, page.number + 2 < page.totalPages ? page.number + 2 : page.totalPages-1)}"
class="page-item" th:classappend="${i == page.number} ? 'active'">
<a class="page-link"
th:href="@{'/employees?' + ${keyword!=null && keyword!=''? 'keyword=' + keyword + '&' : ''} + 'page=' + ${i} + '&size=' + ${page.size}+'&sort='+${sortParam}}"
th:title=${i+1} th:rel=${i+1}>
[[${i+1} ]]
</a>
</li>
<li class="page-item disabled" th:if="${page.number + 2 < page.totalPages}">
<a class="page-link" href="#">...</a>
</li>
<li class="page-item font-weight-bold" th:classappend="${page.number == page.totalPages-1} ? 'disabled'">
<a class="page-link"
th:href="@{'/employees?' + ${keyword!=null && keyword!=''? 'keyword=' + keyword + '&' : ''} + 'page=' + ${page.number + 1} + '&size=' + ${page.size}+'&sort='+${sortParam}}"
title="Next Page" rel="Next Page">
后一页
</a>
</li>
<li class="page-item" th:classappend="${page.number == page.totalPages-1} ? 'disabled'">
<a class="page-link"
th:href="@{'/employees?' + ${keyword!=null && keyword!=''? 'keyword=' + keyword + '&' : ''} + 'page=' +${page.totalPages-1}+ '&size=' + ${page.size}+'&sort='+${sortParam}}"
title="Last Page" rel="Last Page">
最后页
</a>
</li>
</ul>
</nav>
</div>
</div>
<a href="#" class="back-to-top d-flex align-items-center justify-content-center"><i
class="bi bi-arrow-up-short"></i></a>
<script type="text/javascript" th:src="@{/webjars/jquery/jquery.min.js}"></script>
<script type="text/javascript" th:src="@{/webjars/bootstrap/js/bootstrap.bundle.min.js}"></script>
<script type="text/javascript" th:src="@{/assets/js/main.js}" ></script>
<script type="text/javascript">
$(document).ready(function () {
$(".btn-delete").on("click", function (e) {
e.preventDefault();
link = $(this);
tutorialTitle = link.attr("tutorialTitle");
$("#yesBtn").attr("href", link.attr("href"));
$("#confirmText").html("Do you want to delete the Tutorial \<strong\>" + tutorialTitle + "\<\/strong\>?");
var myModal = new bootstrap.Modal(document.getElementById('confirmModal'), {
keyboard: false
});
myModal.toggle();
});
$("#btnClear").on("click", function (e) {
e.preventDefault();
$("#keyword").text("");
window.location = "[[@{/employees}]]";
});
});
function changePageSize() {
$("#searchForm").submit();
}
</script>
</body>
</html>
添加搜索/过滤功能
package com.example;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
public interface EmployeeRepository extends JpaRepository<Employee, Long> {
@Query("SELECT e FROM Employee e WHERE CONCAT(e.name, ' ', e.dept) LIKE %?1%")
public Page<Employee> search(String keyword, Pageable pageable);
}
package com.example;
import java.util.List;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.repository.query.Param;
import org.springframework.data.web.PageableDefault;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class EmployeeController {
@Autowired
private EmployeeRepository repository;
@GetMapping("/employees")
public String getEmployees(@Param("keyword") String keyword, @PageableDefault(size = 10, sort = "id") Pageable pageable,
Model model) {
if (keyword == null) {
Page<Employee> page = repository.findAll(pageable);
List<Sort.Order> sortOrders = page.getSort().stream().collect(Collectors.toList());
if (sortOrders.size() > 0) {
Sort.Order order = sortOrders.get(0);
model.addAttribute("sortProperty", order.getProperty());
model.addAttribute("sortDesc", order.getDirection() == Sort.Direction.DESC);
}
model.addAttribute("page", page);
} else {
Page<Employee> page = repository.search(keyword, pageable);
List<Sort.Order> sortOrders = page.getSort().stream().collect(Collectors.toList());
if (sortOrders.size() > 0) {
Sort.Order order = sortOrders.get(0);
model.addAttribute("sortProperty", order.getProperty());
model.addAttribute("sortDesc", order.getDirection() == Sort.Direction.DESC);
}
model.addAttribute("keyword", keyword);
model.addAttribute("page", page);
}
return "employee-page";
}
}
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org">
<head>
<link rel="icon" th:href="@{/assets/img/favicon.png}" />
<link rel="stylesheet" type="text/css" th:href="@{/webjars/bootstrap/css/bootstrap.min.css}" />
<link rel="stylesheet" type="text/css" th:href="@{/webjars/font-awesome/css/all.min.css}"/>
<link rel="stylesheet" type="text/css" th:href="@{/webjars/bootstrap-icons/font/bootstrap-icons.css}"/>
<link rel="stylesheet" type="text/css" th:href="@{/assets/css/style.css}" />
<link rel="stylesheet" type="text/css" th:href="@{/css/style.css}" />
<script type="text/javascript" th:src="@{/webjars/jquery/jquery.min.js}"></script>
<script type="text/javascript" th:src="@{/webjars/bootstrap/js/bootstrap.bundle.min.js}"></script>
<script th:inline="javascript">
//thymeleaf to javascript variables
/*<![CDATA[*/
var sortProperty = /*[[${sortProperty}]]*/ null;
var sortDesc = /*[[${sortDesc}]]*/ null;
var currentPage = /*[[${page.number}]]*/ 0;
var pageSize = /*[[${page.size}]]*/ 0;
/*]]>*/
$(document).ready(function () {
//show up/down arrows
$("table#emp-table thead th").each(function () {
var head = $(this);
if (head.attr('data-sort-prop') == sortProperty) {
head.append(sortDesc ? '▾' : '▴');
}
});
//set click action, reload page on clicking with all query params
$("table#emp-table thead th").click(function () {
var headerSortPropName = $(this).attr("data-sort-prop");
if (headerSortPropName == sortProperty) {
window.location.href = window.location.pathname +
'?page=' + currentPage + '&size=' + pageSize + '&sort=' + headerSortPropName + ',' +
(sortDesc ? 'asc' : 'desc');
} else {
window.location.href = window.location.pathname +
'?page=' + currentPage + '&size=' + pageSize + '&sort=' + headerSortPropName + ',asc';
}
});
});
</script>
<style>
table{
width:100%;
}
table td, table th {
border: 1px solid grey;
}
table th {
user-select: none;
background: #eee;
}
table tr th:first-child{
width:100px;
}
table tr th:nth-child(3){
width:150px;
}
table tr th:nth-child(4){
width:150px;
}
.pagination-div{
user-select: none;
}
.pagination-div span{
border-radius:3px;
border:1px solid #999;
padding:5px;
margin:10px 0px 0px 10px;
display:inline-block
}
span.selected{
background:#ccf;
}
</style>
</head>
<body>
<h2>Employees</h2>
<div class="my-3 ">
<div class="row d-flex flex-row">
<form th:action="@{/employees}" id="searchForm" >
<div class="row">
<div class="col-sm-8">
<div class="col-md-12 mt-2">
<div class="search">
<i class="fa fa-search"></i>
<input id="keyword" type="search" name="keyword" th:value="${keyword}" required class="form-control"
placeholder="Enter keyword">
<button type="submit" class="btn btn-secondary">搜索</button>
</div>
</div>
</div>
<div class="col-sm-3">
<div class="col-md-12 input-group mt-2">
<div class="input-group-prepend">
<label class="input-group-text" for="pageSize">每页显示行数:</label>
</div>
<select form="searchForm" name="size" th:value="${pageSize}" onchange="changePageSize()" class="size-select"
id="pageSize">
<option th:each="s : ${ {10, 25, 50} }" th:value="${s}" th:text="${s}" th:selected="${s == page.size}"></option>
</select>
</div>
</div>
<div class="col-sm-1">
<div class="col-md-12 mt-2">
<button id="btnClear" class="btn btn-info">重置</button>
</div>
</div>
</div>
</form>
</div>
</div>
<table id="emp-table">
<thead>
<tr>
<th data-sort-prop="id">Id</th>
<th data-sort-prop="name">Name</th>
<th data-sort-prop="dept">Department</th>
<th data-sort-prop="salary">Salary</th>
</tr>
</thead>
<tr th:each="employee : ${page.content}">
<td th:text="${employee.id}"></td>
<td th:text="${employee.name}"></td>
<td th:text="${employee.dept}"></td>
<td th:text="${employee.salary}"></td>
</tr>
</table>
<div class="row">
<div class="col-sm-6">
<div th:if="${page.totalPages > 0}">
总计<span th:text="${page.totalElements}">99</span>行,当前显示<span th:text="(${page.number} )*${page.size} +1">1</span> -
<span th:text="(${page.number} )*${page.size} +${page.numberOfElements}">5</span>行 总计<span th:text="${page.totalPages}">99</span>页
</div>
</div>
<div class="col-sm-6" th:with="sortParam=${sortProperty+','+(sortDesc?'desc':'asc')}">
<nav aria-label="Pagination" th:if="${page.totalPages > 0}">
<ul class="pagination justify-content-center">
<li class="page-item" th:classappend="${page.number == 0} ? 'disabled'">
<a class="page-link"
th:href="@{'/employees?' + ${keyword!=null && keyword!=''? 'keyword=' + keyword + '&' : ''} + 'page=' +'0'+ '&size=' + ${page.size}+'&sort='+${sortParam}}"
title="First Page" rel="First Page">
最前页
</a>
</li>
<li class="page-item font-weight-bold" th:classappend="${page.number == 0} ? 'disabled'">
<a class="page-link"
th:href="@{'/employees?' + ${keyword!=null && keyword!=''? 'keyword=' + keyword + '&' : ''} + 'page=' + ${page.number - 1} + '&size=' + ${page.size}+'&sort='+${sortParam}}"
title="Previous Page" rel="Previous Page">
前一页
</a>
</li>
<li class="page-item disabled" th:if="${page.number - 2 > 0}">
<a class="page-link" href="#">...</a>
</li>
<li
th:each="i : ${#numbers.sequence(page.number > 2 ? page.number - 2 : 0, page.number + 2 < page.totalPages ? page.number + 2 : page.totalPages-1)}"
class="page-item" th:classappend="${i == page.number} ? 'active'">
<a class="page-link"
th:href="@{'/employees?' + ${keyword!=null && keyword!=''? 'keyword=' + keyword + '&' : ''} + 'page=' + ${i} + '&size=' + ${page.size}+'&sort='+${sortParam}}"
th:title=${i+1} th:rel=${i+1}>
[[${i+1} ]]
</a>
</li>
<li class="page-item disabled" th:if="${page.number + 2 < page.totalPages}">
<a class="page-link" href="#">...</a>
</li>
<li class="page-item font-weight-bold" th:classappend="${page.number == page.totalPages-1} ? 'disabled'">
<a class="page-link"
th:href="@{'/employees?' + ${keyword!=null && keyword!=''? 'keyword=' + keyword + '&' : ''} + 'page=' + ${page.number + 1} + '&size=' + ${page.size}+'&sort='+${sortParam}}"
title="Next Page" rel="Next Page">
后一页
</a>
</li>
<li class="page-item" th:classappend="${page.number == page.totalPages-1} ? 'disabled'">
<a class="page-link"
th:href="@{'/employees?' + ${keyword!=null && keyword!=''? 'keyword=' + keyword + '&' : ''} + 'page=' +${page.totalPages-1}+ '&size=' + ${page.size}+'&sort='+${sortParam}}"
title="Last Page" rel="Last Page">
最后页
</a>
</li>
</ul>
</nav>
</div>
</div>
<a href="#" class="back-to-top d-flex align-items-center justify-content-center"><i
class="bi bi-arrow-up-short"></i></a>
<script type="text/javascript" th:src="@{/webjars/jquery/jquery.min.js}"></script>
<script type="text/javascript" th:src="@{/webjars/bootstrap/js/bootstrap.bundle.min.js}"></script>
<script type="text/javascript" th:src="@{/assets/js/main.js}" ></script>
<script type="text/javascript">
$(document).ready(function () {
$(".btn-delete").on("click", function (e) {
e.preventDefault();
link = $(this);
tutorialTitle = link.attr("tutorialTitle");
$("#yesBtn").attr("href", link.attr("href"));
$("#confirmText").html("Do you want to delete the Tutorial \<strong\>" + tutorialTitle + "\<\/strong\>?");
var myModal = new bootstrap.Modal(document.getElementById('confirmModal'), {
keyboard: false
});
myModal.toggle();
});
$("#btnClear").on("click", function (e) {
e.preventDefault();
$("#keyword").text("");
window.location = "[[@{/employees}]]";
});
});
function changePageSize() {
$("#searchForm").submit();
}
</script>
</body>
</html>
示例项目
https://www.logicbig.com/tutorials/spring-framework/spring-data/sorting-and-pagination/spring-data-jpa-sorting-and-pagination.zip