以下是 原生 SQL 分页、PageHelper 的 Page 对象和 PageInfo 对象 三种分页查询方式的详细实现讲解,包含代码示例和对比分析。
一、原生 SQL 分页(不依赖 PageHelper)
1. 实现步骤
SQL 分页语法:手动编写 LIMIT offset, pageSize。总记录数查询:单独编写 COUNT(*) SQL。参数计算:手动计算 offset = (pageNum - 1) * pageSize。
2. 代码示例
// 1. Mapper 接口
public interface EmpMapper {
// 分页查询数据
@Select("SELECT * FROM emp WHERE dept_id = #{deptId} ORDER BY id DESC LIMIT #{offset}, #{pageSize}")
List
// 查询总记录数
@Select("SELECT COUNT(*) FROM emp WHERE dept_id = #{deptId}")
long countTotal(@Param("deptId") Long deptId);
}
// 2. Service 层
public PageResult
int pageNum = dto.getPageNum();
int pageSize = dto.getPageSize();
int offset = (pageNum - 1) * pageSize;
// 查询当前页数据
List
// 查询总记录数
long total = empMapper.countTotal(dto.getDeptId());
return new PageResult<>(total, data);
}
// 3. 分页结果类
public class PageResult
private long total;
private List
// 其他字段...
}
3. 优缺点
优点:无第三方依赖,灵活控制 SQL。缺点:需手动处理分页参数和总条数查询,代码冗余。
二、PageHelper 的 Page 对象
1. 实现步骤
启动分页:调用 PageHelper.startPage(pageNum, pageSize)。查询数据:直接执行查询,返回 Page
2. 代码示例
// 1. Mapper 接口(无需分页参数)
public interface EmpMapper {
@Select("SELECT * FROM emp WHERE dept_id = #{deptId} ORDER BY id DESC")
List
}
// 2. Service 层
public Page
PageHelper.startPage(dto.getPageNum(), dto.getPageSize());
List
return (Page
}
// 3. Controller 层
@GetMapping("/emps")
public PageResult
Page
return new PageResult<>(page.getTotal(), page.getResult());
}
3. 关键点
强制转换:Page
4. 优缺点
优点:简化分页参数处理,自动生成 COUNT 查询。缺点:需强制类型转换,返回的 Page 对象包含数据库方言细节。
三、PageHelper 的 PageInfo 对象
1. 实现步骤
启动分页:PageHelper.startPage(pageNum, pageSize)。查询数据:返回普通 List
2. 代码示例
// 1. Service 层
public PageInfo
PageHelper.startPage(dto.getPageNum(), dto.getPageSize());
List
return new PageInfo<>(list);
}
// 2. Controller 层
@GetMapping("/emps")
public PageInfo
return empService.queryEmpByPage(dto);
}
3. PageInfo 核心字段
public class PageInfo
private int pageNum; // 当前页码
private int pageSize; // 每页数量
private long total; // 总记录数
private int pages; // 总页数
private List
private boolean hasNextPage; // 是否有下一页
// 其他字段...
}
4. 优缺点
优点:开箱即用,封装完整分页信息,无需手动转换。缺点:返回字段固定,若需自定义字段仍需二次封装。
四、三种方式对比
维度原生 SQL 分页PageHelper + PagePageHelper + PageInfo依赖无PageHelper 插件PageHelper 插件代码量多(手动处理分页逻辑)中等(需类型转换)少(直接返回 PageInfo)分页信息需手动封装包含基本分页字段(total、list)包含完整分页字段(pages、hasNext等)灵活性高(可完全控制 SQL)中(依赖插件自动分页)低(字段固定)适用场景简单分页或禁用第三方库时需要快速实现分页需要完整分页信息且减少代码
五、最佳实践建议
推荐 PageInfo:
适合大多数场景,减少重复代码,直接返回完整分页信息。示例:public PageInfo
PageHelper.startPage(dto.getPageNum(), dto.getPageSize());
List
return new PageInfo<>(list);
}
自定义分页实体类:
若前端需要特定字段名(如 current 代替 pageNum),可自定义 PageResult:public class PageResult
@JsonProperty("current")
private int pageNum;
@JsonProperty("size")
private int pageSize;
private long total;
private List
}
性能优化:
大数据量分页时,在 startPage 中指定 count 优化:PageHelper.startPage(pageNum, pageSize, "COUNT(id)");
参数校验:
int pageNum = dto.getPageNum() <= 0 ? 1 : dto.getPageNum();
int pageSize = dto.getPageSize() > 100 ? 100 : dto.getPageSize();
通过以上三种方式,可根据项目需求灵活选择分页实现方案!