本文目录导读:

- 核心分层结构(基于 MVC + Service + DAO)
- 边界情况与规范处理
- 完整调用流程图示(规整版)
- 推荐依赖与版本(2024+ 常用)
- 高级规整要点(适合复杂项目)
- 总结:规整流程 = 统一对象 + 分层职责 + 参数校验 + 防止溢出 + VO转换
在Java开发中,规整分页调用流程的核心目标通常是:解耦、复用、清晰、避免内存溢出。
以下从架构分层、通用工具类、前端对接三个维度,给出一种业界常用的规整方案。
核心分层结构(基于 MVC + Service + DAO)
定义统一的分页请求与响应对象
通用请求对象(避免每个接口重复定义 pageNum, pageSize)
// PageRequest.java
@Data
public class PageRequest {
private int pageNum = 1;
private int pageSize = 10;
// 可选的排序字段
private String orderBy;
private boolean asc = true;
}
通用响应对象(封装总记录数、数据列表)
// PageResult.java
@Data
@AllArgsConstructor
@NoArgsConstructor
public class PageResult<T> {
private long total; // 总记录数
private int pageNum; // 当前页码
private int pageSize; // 每页大小
private List<T> list; // 当前页数据
private int pages; // 总页数(可计算得出)
}
Controller 层:只负责参数校验和结果返回
@RestController
@RequestMapping("/api/users")
public class UserController {
@Autowired
private UserService userService;
@GetMapping
public ResponseEntity<PageResult<UserVO>> listUsers(PageRequest pageRequest,
@RequestParam(required = false) String name) {
// 1. 参数校验(可选,PageRequest可加@Valid)
// 2. 调用Service
PageResult<UserVO> result = userService.queryUsers(pageRequest, name);
return ResponseEntity.ok(result);
}
}
Service 层:核心业务逻辑 + 分页转换
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
@Autowired
private UserConverter userConverter; // 使用 MapStruct 或手动转换
public PageResult<UserVO> queryUsers(PageRequest pageRequest, String name) {
// 方案一:使用 MyBatis-Plus 分页插件(推荐)
Page<User> page = new Page<>(pageRequest.getPageNum(), pageRequest.getPageSize());
LambdaQueryWrapper<User> wrapper = Wrappers.lambdaQuery();
if (StringUtils.isNotBlank(name)) {
wrapper.like(User::getName, name);
}
Page<User> userPage = userMapper.selectPage(page, wrapper);
// 将 PO 转换为 VO(脱敏、移除敏感字段)
List<UserVO> voList = userConverter.toVOList(userPage.getRecords());
return new PageResult<>(
userPage.getTotal(),
userPage.getCurrent(),
userPage.getSize(),
voList,
(int) userPage.getPages()
);
}
}
Mapper 层(DAO)
// 使用 MyBatis-Plus 时:只需继承 BaseMapper
public interface UserMapper extends BaseMapper<User> {
// 不需要额外写分页SQL,MyBatis-Plus 自动处理
}
如果使用 原生 MyBatis + PageHelper:
<select id="selectUsers" resultType="User">
SELECT * FROM user
<where>
<if test="name != null and name != ''">
AND name LIKE CONCAT('%', #{name}, '%')
</if>
</where>
</select>
// Service中 PageHelper.startPage(pageRequest.getPageNum(), pageRequest.getPageSize()); List<User> users = userMapper.selectUsers(name); PageInfo<User> pageInfo = new PageInfo<>(users); // 转换为VO后构造PageResult
边界情况与规范处理
参数校验(防止异常页数)
// 在 Controller 或 全局过滤器中
public class PageValidator {
public static void validate(PageRequest pageRequest) {
if (pageRequest.getPageNum() < 1) {
pageRequest.setPageNum(1);
}
if (pageRequest.getPageSize() < 1) {
pageRequest.setPageSize(10);
}
// 防止大尺寸攻击或内存溢出
if (pageRequest.getPageSize() > 1000) {
pageRequest.setPageSize(1000);
}
}
}
优化建议(避免 count 性能问题)
- 如果明确只查一页数据且不需要知道总数(如移动端“加载更多”),可提供
NoCountPageRequest,SQL 中使用limit pageSize+1来判断是否有下一页。 - 大字段分页:如果列表不需要大文本字段(如
content),在 Mapper 中指定resultMap排除该字段。
前端期望的统一格式(建议遵守)
{
"code": 200,
"message": "success",
"data": {
"total": 100,
"pageNum": 1,
"pageSize": 10,
"pages": 10,
"list": [ ... ]
}
}
使用统一响应体包装 PageResult:
public class R<T> {
private int code;
private String message;
private T data;
}
完整调用流程图示(规整版)
客户端(前端)
│
▼
Controller(接收 PageRequest + 过滤条件)
│
├── 参数校验(pageNum, pageSize 范围限制)
│
▼
Service(业务逻辑层)
│
├── 1. 转换/构建查询条件(DTO → Entity)
├── 2. 调用 Mapper 分页查询(MyBatis-Plus Page / PageHelper)
├── 3. 从分页对象获取 total + records
├── 4. 将 PO 转为 VO(脱敏、格式化)
└── 5. 构造 PageResult<T> 返回
│
▼
Controller(将 PageResult 放入统一响应体 R 中)
│
▼
返回 JSON 给前端
推荐依赖与版本(2024+ 常用)
| 框架 | 推荐版本 | 分页方式 |
|---|---|---|
| Spring Boot 3.x / Spring 6 | 3+ | |
| MyBatis-Plus | 5.5+ | Page<T> 自动拦截 |
| PageHelper | x(适配 Spring Boot 3) | PageHelper.startPage() |
| MapStruct (DTO/VO转换) | 5+ | 自动生成转换代码 |
| Hibernate Validator | 6+ / 8+ | 参数校验 |
高级规整要点(适合复杂项目)
- 分页与过滤条件解耦:不要将
pageNum放在业务DTO中,单独使用PageRequest。 - 多表关联分页:如果使用 MyBatis-Plus
selectPage无法处理复杂JOIN,可以:- 先查主表ID列表进行分页
- 再
IN查关联数据 - 在内存中组装(注意数据量不能太大)
- 超大分页优化:使用覆盖索引 + 延迟关联(即先查ID,再JOIN主表)。
- 避免 N+1 问题:在循环中调用其他表的查询,应在分页前一次性查好。
规整流程 = 统一对象 + 分层职责 + 参数校验 + 防止溢出 + VO转换
如果需要代码模板(比如通用分页Service基类、自定义分页注解),可以进一步说明,我可以给出可复用的代码段。