本文目录导读:

针对Java分页执行流程的规整化,核心在于解耦(将分页逻辑与业务逻辑分离)、统一(使用标准接口与DTO)和高效(避免内存分页,使用数据库原生分页)。
以下是经过业界验证的规整化方案,主要涵盖后端(Spring Boot + MyBatis/MyBatis-Plus)的典型场景。
规整化的核心原则
- 统一参数与返回:所有分页接口统一接收
PageParam(当前页、每页大小),返回PageResult(分页数据、总条数、总页数等)。 - SQL层分页:坚决避免
List.stream().skip().limit()这种方式(性能极差),必须使用数据库的LIMIT/OFFSET或游标分页。 - 使用现成ORM框架:优先使用 MyBatis-Plus / JPA 的分页机制,避免手动拼装
PageHelper或手写RowBounds(容易出问题)。 - DTO与VO分离:数据库实体(DO)不应直接返回前端,需要转换为视图对象(VO)。
标准分页执行流程(MyBatis-Plus为例)
分页入参出参定义(规整的基础)
// 1. 通用分页请求参数
@Data
public class PageParam {
@Min(1) private Integer pageNo = 1;
@Min(1) @Max(500) private Integer pageSize = 20;
// 可扩展排序字段:private String orderBy; private Boolean asc;
}
// 2. 通用分页返回结果
@Data
public class PageResult<T> {
private List<T> records;
private Integer pageNo;
private Integer pageSize;
private Long total;
private Integer pages; // 总页数
public PageResult(List<T> records, Long total, PageParam param) {
this.records = records;
this.total = total;
this.pageNo = param.getPageNo();
this.pageSize = param.getPageSize();
this.pages = (int) Math.ceil((double) total / param.getPageSize());
}
}
Controller层:接收参数并封装
@RestController
public class UserController {
@GetMapping("/users")
// 统一使用 @Validated 校验分页参数
public Result<PageResult<UserVO>> listUsers(@Validated PageParam pageParam,
@RequestParam(required = false) String name) {
// 调用Service层,Controller只负责接收和返回,不做业务处理
PageResult<UserVO> page = userService.queryUserPage(pageParam, name);
return Result.success(page);
}
}
/*
优点:Controller 代码极短,不感知 MyBatis-Plus 的 Page 对象
*/
Service层:转换与装配
@Service
public class UserServiceImpl implements UserService {
@Resource
private UserMapper userMapper;
@Override
public PageResult<UserVO> queryUserPage(PageParam pageParam, String name) {
// 1. 构建 MyBatis-Plus 的 Page 对象(这一步需要 ORM 框架的分页插件)
// Page<UserDO> 是 MyBatis-Plus 提供的,内部会自动处理 limit
Page<UserDO> mybatisPage = new Page<>(pageParam.getPageNo(), pageParam.getPageSize());
// 2. 执行分页查询(传入 Page 对象,无需手动计算 offset)
// Mapper 方法返回 Page<UserDO>,其中已包含 total 和 records
Page<UserDO> resultPage = userMapper.selectUserPage(mybatisPage, name);
// 3. 将 DO 转换为 VO(规整的关键步骤)
List<UserVO> voList = resultPage.getRecords().stream()
.map(this::convertToVO) // 可使用 BeanUtils.copyProperties 或 MapStruct
.collect(Collectors.toList());
// 4. 返回统一的 PageResult (解耦,不暴露 MyBatis 的 Page 给 Controller)
return new PageResult<>(voList, resultPage.getTotal(), pageParam);
}
}
Mapper层:纯 SQL 或 MyBatis-Plus 语法
MyBatis-Plus 方法(推荐,无需写XML)
// Mapper 接口
@Mapper
public interface UserMapper extends BaseMapper<UserDO> {
// 使用 MyBatis-Plus 的 QueryWrapper 分页
default Page<UserDO> selectUserPage(Page<UserDO> page, String name) {
return this.selectPage(page,
new LambdaQueryWrapper<UserDO>()
.like(StringUtils.isNotBlank(name), UserDO::getName, name)
.orderByDesc(UserDO::getCreateTime));
}
}
// 注意:需要在配置类中启用 MyBatis-Plus 分页插件
@Configuration
public class MyBatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
手写 XML(复杂查询时使用)
<!-- UserMapper.xml -->
<select id="selectUserPage" resultType="com.example.entity.UserDO">
SELECT * FROM user
<where>
<if test="name != null and name != ''">
name LIKE CONCAT('%', #{name}, '%')
</if>
</where>
ORDER BY create_time DESC
<!-- 这里不需要写 limit,MyBatis-Plus 分页插件会自动在尾部追加 LIMIT 和 COUNT 查询 -->
</select>
规整化流程的“避坑”与进阶
计算总条数的性能优化(count优化)
- 问题:MyBatis-Plus 默认会执行
SELECT COUNT(*)全表扫描。 - 优化方案:
- 根据业务判断:如果
pageSize很小(如固定20条),且用户极少翻到第1000页,可配置page.setOptimizeCountSql(false)。 - 如果数据量极大(如千万级),弃用传统分页,改用 游标分页(Keyset Pagination):基于上一页最后一条记录的ID查询
WHERE id > ? LIMIT 20,这种方式无需count,且性能稳定O(1)。 - 规整建议:在
PageResult中加入isOptimized标识,前端判断如果总页数过大,不显示具体总条数。
- 根据业务判断:如果
统一处理排序
- 不要让每个Mapper都写死排序字段。
- 规整做法:在
PageParam中增加orderBy和asc字段,Service层统一构建OrderItem后设置到Page对象。
批量操作与分页冲突
- 问题:
PageHelper的ThreadLocal机制在异步或批处理时易导致分页错乱。 - 规整建议:全局统一使用 MyBatis-Plus 的
Page对象作为方法参数(显式传递),彻底弃用PageHelper.startPage()。
最终效果对比
| 场景 | 混乱的写法 | 规整写法 |
|---|---|---|
| Controller方法 | PageInfo<User> 直接返回给前端 |
返回通用的 PageResult<UserVO> |
| 参数校验 | 手动判断 pageNo > 0 |
使用 @Validated + PageParam统一校验 |
| 分页逻辑 | 在Service里自己算 offset + limit | 传入 MyBatis-Plus 的 Page 对象,插件自动处理 |
| 返回数据 | 返回的list包含DO实体,暴露密码等敏感字段 |
VO转换,只返回前端需要的字段 |
| 耦合度 | Service里new PageInfo()依赖了PageHelper |
Service只依赖自己的DTO和MyBatis-Plus的Page,容易替换 |
规整的Java分页流程 = PageParam作为入参 → ORM分页插件执行 → DO转VO → PageResult作为出参。
只要团队遵循这个流程,任何后端成员都能快速看懂、修改分页代码,且不会出现慢SQL或数据泄露问题。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播