写在前面:大家好!我是。如果博客中有不足或者的错误的地方欢迎在评论区或者私信我指正,感谢大家的不吝赐教。我的唯一博客更新地址是:https://ac-fun.blog.csdn.net/。非常感谢大家的支持。一起加油,冲鸭!
用知识改变命运,用知识成就未来!加油 (? ??o??)? (? ??o??)?
分页查询是一种常见的数据库查询技术,用于将查询结果分成多个页面展示,而不是一次性返回所有数据。使用分页查询主要是为了减少数据库压力、减少网络传输数据量、提高系统的稳定性、提高客户体验。
一次性查询全部数据(例如百万条记录)会占用大量的资源(CPU、内存、I/O),导致响应变慢甚至系统崩溃。分页后,每次仅查询少量数据(如每页100条),可以显著降低负载。
分页查询每次只传输当前页的数据,相比于全表查询会极大的减少网络传输的数据量,降低网络带宽的占用。
后端服务处理分页查询时,单次处理的数据量可控,避免因一次性加载大数据导致内存耗尽出现 OOM 问题。对于前端也由于无需一次性渲染大量的数据而减少了内存崩溃的风险。
分页查询由于单次查询的数据量少,后端与前端可以快速的处理相关的数据。用户无需进行长时间的等待,极大的提高了客户的体验。
如果不使用分页查询相关的插件需要我们自己计算分页查询的偏移量offset,还需要手动查询结果集以及数据总条数,并且在相关的 Mapper.xml 中定义分页查询的 SQL 语句。主要实现步骤如下:
手动分页查询需要我们通过在 SQL 语句中添加分页相关的语法来实现,例如在 MySQL 中的语法:
SELECT * FROM users LIMIT #{pageSize} offset #{offset};
其中,#{offset} 表示偏移量,但是前端的分页查询请求中一般只有查询第几页 page 和每页的大小 pageSize。需要我们先计算一下偏移量是多少。
需要注意前端传的 page 是从 0 开始的还是从 1 开始的。
/**
* 分页查询结果集
* @param page
* @param offset
* @param name
* @param categoryId
* @param status
* @return
*/
List pageQuery(int page, int offset, String name, Integer categoryId, Integer status);
/**
* 查询总条数
* @return
*/
int getTotalSize();
insert into dish (name, category_id, price, image, description, status, create_time, update_time, create_user, update_user)
values (#{name}, #{categoryId}, #{price}, #{image}, #{description},#{status}, #{createTime}, #{updateTime}, #{createUser}, #{updateUser})
在开发过程中 由外而内 进行开发效率会更高一些,我们一般不需要先写 Mapper.xml 中的 SQL语句,再定义Mapper接口中的方法,然后再通过 Service类 中进行调用。
一般会从 Service类 开始写起,然后再通过编辑器的快捷方式帮助我们生成相关的代码,再一层一层的实现。
@GetMapping("/page")
@ApiOperation("菜品分页查询")
public Result pageQuery(DishPageQueryDTO dishPageQueryDTO) {
log.info("菜品分页查询开始[{}]", dishPageQueryDTO);
return dishService.pageQuery(dishPageQueryDTO);
}
@Override
public Result pageQuery(DishPageQueryDTO dishPageQueryDTO) {
// 计算偏移量
int offset = (dishPageQueryDTO.getPage() - 1) * dishPageQueryDTO.getPageSize();
// 查询当前页的数据
List dishVOList = dishMapper.pageQuery(dishPageQueryDTO.getPageSize(), offset, dishPageQueryDTO.getName(),
dishPageQueryDTO.getCategoryId(), dishPageQueryDTO.getStatus()); 
// 查询数据库中的总条数
int total = dishMapper.getTotalSize();
PageResult pageResult = new PageResult();
pageResult.setTotal(total);
pageResult.setRecords(dishVOList);
log.info("分页查询结果为[{}]", pageResult);
return Result.success(pageResult);
}
/**
* 分页查询结果集
* @param page
* @param offset
* @param name
* @param categoryId
* @param status
* @return
*/
List pageQuery(int page, int offset, String name, Integer categoryId, Integer status);
/**
* 查询总条数
* @return
*/
int getTotalSize();
insert into dish (name, category_id, price, image, description, status, create_time, update_time, create_user, update_user)
values (#{name}, #{categoryId}, #{price}, #{image}, #{description},#{status}, #{createTime}, #{updateTime}, #{createUser}, #{updateUser})
使用分页插件可以极大的简化分页查询实现。虽然实现的主要原理还是通过原始实现方式中提到的逻辑,但是通过分页插件我们就可以少写很多代码而且一般分页插件(例如 PageHelper) 会通过动态 SQL 的构建和优化,能够有效避免传统分页方法的性能问题。
使用插件进行分页查询只需要修改一下上述原始实现方式的Service类 及 xml 文件中的 SQL 语句即可。
com.github.pagehelper
pagehelper-spring-boot-starter
@Override
public Result pageQuery(DishPageQueryDTO dishPageQueryDTO) {
PageHelper.startPage(dishPageQueryDTO.getPage(), dishPageQueryDTO.getPageSize());
Page page = dishMapper.pageHelperQuery(dishPageQueryDTO);
PageResult pageResult = new PageResult();
pageResult.setTotal(page.getTotal());
pageResult.setRecords(page.getResult());
log.info("分页查询结果为[{}]", pageResult);
return Result.success(pageResult);
}
使用分页插件进行分页不需要手动计算分页查询的偏移量,在写SQL语句时也不需要显式地使用 LIMIT 和 OFFSET 来实现分页。而且分页插件也会直接将查询的结果集和总条数封装到 Page对象 中,不需要我们手动的查询结果集和总条数。