一、分页查询
1. 实现机制
假分页即全量从数据库读取,再利用前端组件进行分页展示。这么做虽然最简单粗暴,但面对大数据量不仅对数据库压力大,而且前端一次性将大量数据载入内存中会大大降低页面响应速度。
如 Antd
中的 a-table
默认就是假分页,后端数据一次性加载到前端页面再由前端实现渲染,适合数据量不大的常见,这里就不具体举例了。
相应对假分页即真分页,故顾名思义即通过如 MySQL
中 limit
关键字降低数据库压力,在大数据量时能够有更高的效率但是实现相对更为复杂。
在数据库查询时通过 limit
关键字实现分页查询,不同数据库的关键字不同这里以 MySQL
举例,如下将查询的数据区间为 [2, 5]
,即第 2
条数据至第 5
条。
select
id, name, gender, create_time
from sys_user
limit 2, 5;
对于前端表格分页通常存在两个重要参数:PageIndex
与 PageLimit
,分别代表当前页数与每页展示的数量。如 PageIndex = 2, PageLimit = 10
的情况下,查询的数据条数应该为第 11 ~ 20
条数据,即上述 SQL
中 limit
的两个参数计算公式如下:
- 参数一:
(PageIndex - 1) * PageLimit + 1
- 参数二:
PageLimit
2. 依赖引入
为了实现数据分页功能在工程中引入下述依赖:
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-commons</artifactId>
</dependency>
3. 数据封装
(1) Service
Spring Boot
中自带了 Page
对象提供了数据分页服务,实现分页数据的对象封装。
注意一点的是我们无需手动根据之前提到的计算公式换算 offset
与 limit
的值,框架已经替我们进行处理。同时在 PageRequest
中 offset
即上述提到 PageIndex
其对应的初始下标为 0
,即 offset = 0, limit = 10
查询的数据条数应该为第 1 ~ 10
条数据。
@Service("sysUserService")
public class SysUserServiceImpl implements SysUserService {
@Override
public Page<SysUser> queryByPage(SysUser sysUser, int offset, int limit) {
// 构造分页条件
PageRequest request = PageRequest.of(offset, limit);
// 分页查询数据
long total = this.sysUserDao.count(sysUser);
List<SysUser> data = sysUserDao.queryAllByLimit(sysUser, request)
// 构造分页对象
Page<SysUser> pageInfo = new PageImpl<>(data, request, total);
return pageInfo;
}
}
@Mapper
public interface SysUserDao {
List<SysUser> queryAllByLimit(SysUser sysUser, @Param("pageable") Pageable pageable);
}
(2) SQL
在 SQL
命令中通过 limit
关键字实现分页查询。
<select id="queryAllByLimit" resultMap="SysUserMap">
select *
from sys_user
<where>
<if test="sysUser.id != null">
and id = #{id}
</if>
<if test="sysUser.username != null and sysUser.username != ''">
and username = #{username}
</if>
<if test="sysUser.password != null and sysUser.password != ''">
and password = #{password}
</if>
</where>
limit #{pageable.offset}, #{pageable.pageSize}
</select>
4. 前端设计
这里以 Antd
表格为例,在标签内添加 pagination
属性。
<a-table
:columns="columns"
:data-source="data"
:pagination="pagination"
:bordered="false"
/>
pagination
具体定义参数如下,在 onShowSizeChange()
与 onChange()
事件中调用之前定义的后端分页查询接口即可。
其中 updatePage()
方法即根据前端变更的页数调用后端中定义的分页查询接口。
data() {
return {
data: [],
tableData: [],
pageRequest: {
offset: 1,
limit: 5
},
pagination: {
total: 0, // 总数,必须先有
defaultCurrent: 1, // 默认当前页数
defaultPageSize: 5, // 默认当前页显示数据的大小
showSizeChanger: true,
showQuickJumper: true,
pageSizeOptions: ["5", "10", "15", "20"],
showTotal: (total) => `共 ${total} 条`, // 显示总数
// 改变每页数量触发
onShowSizeChange: (current, pageSize) => {
this.pagination.defaultCurrent = 1
this.pagination.defaultPageSize = pageSize
this.updatePage(current, pageSize)
},
// 点击页数触发
onChange: (current, size) => {
this.pagination.defaultCurrent = current
this.pagination.defaultPageSize = size
this.updatePage(current, size)
}
}
}
}
二、分页插件
1. 插件介绍
除了 Spring
提供的分页功能外,Pagehelper
同样也是一样使用广泛的分页插件。
同样需要在工程中 pom
文件引入其对应的依赖信息。
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>2.1.0</version>
</dependency>
2. 插件配置
除了引入对应的 maven
依赖外还需要再工程中 application.yml
文件中配置下述属性:
pagehelper:
helperDialect: mysql
reasonable: true
supportMethodsArguments: true
params: count=countSql
3. 使用实例
PageHelper
使用十分简单,只需通过 PageHelper.startPage()
设置分页参数即可,这里的 list()
查询语句中则无需再手动拼接 limit
关键字。
PageHelper
实现原理即通过拦截器识别查询语句,当为查询语句且配置了 startPage()
则会自动为语句拼接 limit
查询。同时 PageHelper.startPage()
基于 ThreadLocal
实现,即每次查询后都将清空 startPage()
配置的值,因此每次分页查询前都需配置 startPage()
属性。
import com.github.pagehelper.PageInfo;
@Service("sysUserService")
public class SysUserServiceImpl implements SysUserService {
@Override
public PageInfo<SysUser> queryByPageHelper(SysUser sysUser, PageDetails pageDetails) {
// 获取分页参数
int offset = pageDetails.getOffset();
int limit = pageDetails.getLimit();
// 开启分页查询,执行查询后失效
PageHelper.startPage(offset, limit);
List<SysUser> list = this.sysUserDao.list(sysUser);
return new PageInfo<>(list);
}
}
默认 PageHelper.startPage()
在执行分页查询时会执行 count
统计总数用于构建 PageInfo
对象,若不需要总行数统计可设置关闭,当设置为 false
时 PageInfo
实例的 count
属性为 -1
。
import com.github.pagehelper.PageInfo;
@Service("sysUserService")
public class SysUserServiceImpl implements SysUserService {
@Override
public PageInfo<SysUser> queryByPageHelper(SysUser sysUser, PageDetails pageDetails) {
// 获取分页参数
int offset = pageDetails.getOffset();
int limit = pageDetails.getLimit();
// 不执行 count 查询
PageHelper.startPage(offset, limit, false);
List<SysUser> list = this.sysUserDao.list(sysUser);
return new PageInfo<>(list);
}
}
如测试表 sys_user
中有 25
条数据,则当 offset=1,limit=3
执行分页查询后返回的数据格式如下:
{
"total": 25,
"list": [
{
"id": "1",
"name": "Adel",
"gender": "Male",
"createTime": "2007-12-17T14:20:42.000+00:00"
},
{
"id": "2",
"name": "Tiffany",
"gender": "Female",
"createTime": "2018-11-10T11:49:46.000+00:00"
},
{
"id": "3",
"name": "Steve",
"gender": "Male",
"createTime": "1992-03-09T11:01:56.000+00:00"
}
],
"pageNum": 1,
"pageSize": 3,
"size": 3,
"startRow": 1,
"endRow": 3,
"pages": 9,
"prePage": 0,
"nextPage": 2,
"isFirstPage": true,
"isLastPage": false,
"hasPreviousPage": false,
"hasNextPage": true,
"navigatePages": 8,
"navigatepageNums": [
1,
2,
3,
4,
5,
6,
7,
8
],
"navigateFirstPage": 1,
"navigateLastPage": 8
}