分页查询的原理是通过将大数据量集分割成较小的部分来返回结果,以减少一次性加载和显示的数量。
接受分页参数:在分页查询中,通常会接收两个关键的参数——当前页码(current)和每页显示的数据量(size)。这些参数决定了用户希望查看哪一部分数据。
计算偏移量:根据当前页码和每页显示数据量,计算出从数据库中哪一行数据开始获取。偏移量公式:
start = (current - 1)* size; start 的值就是数据集中的起始位置。
执行分页查询:在SQL查询中使用 Limit 字句来限制查询返回的记录数。Limit 接受两个参数——偏移量 start 和 每页显示的记录数 size,指示数据库从偏移量处开始,返回指定数量的记录。如:
select * from table_name limit start, size; -- 这条语句将从 start 位置开始获取,获取 size 条记录。
还有 像 PageHelper 或 RowBounds 这样的分页工具,分页查询原理虽然与 Limit 查询类似,但过程更加简化和自动化。通过简化分页原理,自动处理分页参数并返回分页后的数据。
构建查询条件:分页查询中,通常还会结合其他条件(如筛选条件),来进一步缩小查询范围。条件可以通过 where 子句进行过滤,以满足查询要求。
返回结果:数据库执行查询后,将结果集返回给应用程序。程序将结果进行展示,用户使用时可以通过调整页码和每页记录数,查看不同的部分。
通过分页查询,系统可以有效减少内存占用和网络传输的压力,提升响应速度,特别是在数据量非常大的情况下。
1. 在SQL内分页
SQL 使用 Limit。可以直接单表使用,也可以多表实现。
只返回数据,不返回查询总数。
分页查询类:
/*** 分页*/ @Data public class PageShowDto {/** 初始页 */private Integer current;/** 数量 */private Integer size;/** 开始 */private Integer start;}
Service实现类:
/*** 查询** @param pageDto 分页* @param steamDto 条件* @return 信息*/@Overridepublic List<ExerciseSchemeSteamDto> findExerciseType(ExerciseSchemeSteamDto steamDto, PageShowDto pageDto) {pageDto.setStart((pageDto.getCurrent() - 1) * pageDto.getSize());return exerciseSchemeAndSteamMapper.findExerciseSchemeSteamType(steamDto, pageDto);}
current--起始页;
size--每页条数;
pageDto.getCurrent() - 1) * pageDto.getSize()-- 如:(current 3 - 1) * size 10 为 20;此时 start 为 20;
Mappe接口:
/** 查询 */List<ExerciseSchemeSteamDto> findExerciseSchemeSteamType(@Param("steamDto") ExerciseSchemeSteamDto steamDto, @Param("pageDto") PageShowDto pageDto);
XML编写SQL:
<!-- 查询两个表 --> <select id="findExerciseSchemeSteamType"resultType="com.control.interactive.entity.dto.ExerciseSchemeSteamDto">selectes.id,es.stage_name as stageName,es.condition_scenario as conditionScenario,es.red_team_action as redTeamAction,ess.team_id as teamId,ess.team_name as teamName,ess.action_command as actionCommand,ess.scheme_id as schemeIdfrom t_exercise_scheme esleft join t_exercise_scheme_team esson es.id = ess.scheme_id<where><if test="steamDto.stageName != null and steamDto.stageName != ''">es.stage_name like concat('%', #{steamDto.stageName}, '%')</if></where>order by es.id<if test="pageDto != null">limit #{pageDto.size} offset #{pageDto.start}</if></select>
current--起始页;
size--每页条数;
pageDto.getCurrent() - 1) * pageDto.getSize()-- 如:(current 3 - 1) * size 10 为 20;此时 start 为 20;
此时分页查询是从 第21条 数据开始截取,每页 10条 数据。
查询直接传递参数 current 与 size。想进一步使用条件过滤,直接传递相应的字段,如 name-模糊查询等等...
优化返回结果,可以使用 `com.baomidou.mybatisplus.extension.plugins.pagination.Page` 的 Page<> 返回。
2. 使用 PageHelper 插件
PageHelper 是 Mybatis 中常用的分页插件。再查询SQL前,自动处理分页逻辑并为查询语句添加适当的 Limit 字句。
注入依赖:
<!-- PageHelper 分页插件 --><dependency><groupId>com.github.pagehelper</groupId><artifactId>pagehelper</artifactId><version>4.1.6</version></dependency>
PS:注入依赖使用 注意 与 JSqlParser 的版本冲突,根据情况进行升降 pagehelper 版本。
自定义分页查询类,用于前端传值:
/*** 分页*/ @Data public class PageShowDto {/** 初始页 */private Integer current;/** 数量 */private Integer size;}
ServiceImpl:
初始化分页:在调用查询方法前,通过 PageHelper.startPage(current, size) 设置当前页码和每页显示的记录数。
~~ PageHelper.startPage(1, 10);
/*** 查询** @param pageDto 分页* @param steamDto 条件* @return 信息*/@Overridepublic R<PageInfo<ExerciseSchemeSteamDto>> findExercise(PageShowDto pageDto, ExerciseSchemeSteamDto steamDto) {// 分页参数设置 PageHelper.startPage(pageDto.getPageNum(), pageDto.getPageSize());List<ExerciseSchemeSteamDto> exerciseSchemeSteam = exerciseSchemeAndSteamMapper.findExerciseSchemeSteam(steamDto);PageInfo<ExerciseSchemeSteamDto> steamPageInfo = new PageInfo<>(exerciseSchemeSteam);return R.ok(steamPageInfo);}
PageHelper 拦截SQL语句,再执行SQL前自动添加 Limit 和 Offset 子句,便于在数据库表中查询所需数据。
XML SQL 编写:
/** 查询 */List<ExerciseSchemeSteamDto> findExerciseSchemeSteam(@Param("steamDto") ExerciseSchemeSteamDto steamDto);
<!-- 查询--><select id="findExerciseSchemeSteam"resultType="com.hrzn.control.interactive.blue.entity.dto.ExerciseSchemeSteamDto">selectes.id as id,es.stage_name as stageName,es.condition_scenario as conditionScenario,es.red_team_action as redTeamAction,ess.team_id as teamId,ess.team_name as teamName,ess.action_command as actionCommand,ess.scheme_id as schemeIdfrom t_exercise_scheme es left join t_exercise_scheme_team esson es.id = ess.scheme_id<where><if test="steamDto.stageName != null and steamDto.stageName != ''">es.stage_name like concat('%', #{steamDto.stageName}, '%')</if></where>order by es.id;</select>
后台会通过 PageHelper 将SQL 改为 select (字段) from (表名) limit 0, 10;
返回结果:查询完成后,优化查询返回结果可以使用 PageInfo<>,PageHelper 会将结果封装到一个 PageInfo对象,其中包含查询结果、总数据量、总页数等分页信息。
优点:
简化分页代码:只需要调用 startPage() 方法,分页逻辑自动处理。
集成度高:与 Mybatis 无缝集成,直接应用于现有的 Mybatis 查询。
简单使用:https://www.cnblogs.com/warmNest-llb/p/18120512
3. 使用 RowBounds 分页
RowBounds 是 Mybatis 自带的一种分页处理方式,通过逻辑分页(在内存中分页)来控制查询结果的范围。
RowBounds:
构建查询:
/*** 查询** @param schemeSteamPage 条件* @param pageDto 分页* @return true*/@Overridepublic List<ExerciseSchemeSteamDto> findExerciseRowBounds(ExerciseSchemeSteamDto schemeSteamPage, PageShowDto pageDto) {RowBounds rowBounds = new RowBounds((pageDto.getCurrent() - 1) * pageDto.getSize(), pageDto.getSize());return exerciseSchemeAndSteamMapper.findExerciseSchemeSteamRow(schemeSteamPage, rowBounds);}
构建 RowBounds 对象:通过 创建一个 RowBounds 对象,传入偏移量 offsert 和 每页记录数 limit;
new RowBounds((pageDto.getCurrent() - 1) * pageDto.getSize(), pageDto.getSize()) 等价于 new RowBounds(offset, size);
offset 从哪行数据开始,size 多少条数据。
XML 编写 SQL:
/** 查询 */List<ExerciseSchemeSteamDto> findExerciseSchemeSteamRow(@Param("steamDto") ExerciseSchemeSteamDto steamDto, RowBounds rowBounds);
<!-- 演练查询--><select id="findExerciseSchemeSteam"resultType="com.hrzn.control.interactive.blue.entity.dto.ExerciseSchemeSteamDto">selectes.id as id,es.stage_name as stageName,es.condition_scenario as conditionScenario,es.red_team_action as redTeamAction,ess.team_id as teamId,ess.team_name as teamName,ess.action_command as actionCommand,ess.scheme_id as schemeIdfrom t_exercise_scheme es left join t_exercise_scheme_team esson es.id = ess.scheme_id<where><if test="steamDto.stageName != null and steamDto.stageName != ''">es.stage_name like concat('%', #{steamDto.stageName}, '%')</if></where>order by es.id;</select>
查询与分页:Mybatis 执行查询时,将结果集取回后,根据 RowBounds 进行内存级别的分页处理。这种方式下分页逻辑是在应用程序内存中执行的,而不是在SQL查询中添加 Limit 子句。
优点:
无需改动原始SQL:可以直接作用于已有的 Mybatis 查询。
缺点:
适合小数据集:由于是内存中处理分页逻辑,对于大数据集来说性能较差。通常不推荐在大数据集上使用 RowBounds 进行分页。
4. 总结
直接在SQL内分页,简单粗暴。
PageHelper 自动化处理分页查询,拦截并改写 SQL,性能高,适合大数据集,推荐在 Mybatis 中使用。
RowBounds 则是基于内存的分页方式,简单直接,但对于大数据集性能不佳,通常只适合处理小数据集的分页。