Java分页调用流程如何规整

wen java案例 32

本文目录导读:

Java分页调用流程如何规整

  1. 核心分层结构(基于 MVC + Service + DAO)
  2. 边界情况与规范处理
  3. 完整调用流程图示(规整版)
  4. 推荐依赖与版本(2024+ 常用)
  5. 高级规整要点(适合复杂项目)
  6. 总结:规整流程 = 统一对象 + 分层职责 + 参数校验 + 防止溢出 + 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+ 参数校验

高级规整要点(适合复杂项目)

  1. 分页与过滤条件解耦:不要将 pageNum 放在业务DTO中,单独使用 PageRequest
  2. 多表关联分页:如果使用 MyBatis-Plus selectPage 无法处理复杂JOIN,可以:
    • 先查主表ID列表进行分页
    • IN 查关联数据
    • 在内存中组装(注意数据量不能太大)
  3. 超大分页优化:使用覆盖索引 + 延迟关联(即先查ID,再JOIN主表)。
  4. 避免 N+1 问题:在循环中调用其他表的查询,应在分页前一次性查好。

规整流程 = 统一对象 + 分层职责 + 参数校验 + 防止溢出 + VO转换

如果需要代码模板(比如通用分页Service基类、自定义分页注解),可以进一步说明,我可以给出可复用的代码段。

抱歉,评论功能暂时关闭!