当前位置: 首页 > news >正文

Spring + MyBatis/MyBatis-Plus 分页方案(limit分页和游标分页)详解

Spring + MyBatis/MyBatis-Plus 分页方案(limit分页和游标分页)详解

版本说明

  • Spring Boot: 3.1.x
  • MyBatis: 3.5.x
  • MyBatis-Plus: 3.5.x
  • PageHelper: 6.0.x

一、分页方式概述

1. 传统分页(LIMIT/OFFSET)

  • 核心原理:通过 LIMITOFFSET 截取数据片段
  • 适用场景:后台管理系统、需跳页查询的场景
  • 实现方案
    • 手动分页(原生 SQL)
    • PageHelper 插件(推荐)
    • MyBatis-Plus 分页插件(推荐)

2. 游标分页(Cursor-based)

  • 核心原理:基于排序字段游标(如 ID、时间戳)逐页查询
  • 适用场景:移动端无限滚动、实时数据流
  • 实现方案
    • 手动分页(主流方案)
    • 成熟插件:目前无广泛采用的插件,需手动实现

二、传统分页实现(LIMIT/OFFSET)

方案 1:手动分页(原生 SQL)

请求参数类
public class PageParam {private Integer pageNum = 1;  // 当前页码private Integer pageSize = 10; // 每页数量public Integer getOffset() {return (pageNum - 1) * pageSize;}// Getter & Setter
}
Mapper 接口
@Mapper
public interface UserMapper {List<User> selectByPage(@Param("offset") Integer offset, @Param("pageSize") Integer pageSize);Long selectTotalCount();
}
XML 映射文件
<!-- 分页查询 -->
<select id="selectByPage" resultType="User">SELECT * FROM userORDER BY id DESCLIMIT #{offset}, #{pageSize}
</select><!-- 总记录数 -->
<select id="selectTotalCount" resultType="java.lang.Long">SELECT COUNT(*) FROM user
</select>
Service 层
@Service
public class UserService {@Autowiredprivate UserMapper userMapper;public PageResult<User> getUsers(PageParam param) {List<User> users = userMapper.selectByPage(param.getOffset(), param.getPageSize());Long total = userMapper.selectTotalCount();return PageResult.<User>builder().list(users).total(total).pageNum(param.getPageNum()).pageSize(param.getPageSize()).build();}
}

方案 2:PageHelper 插件(推荐)

配置插件
@Configuration
public class PageHelperConfig {@Beanpublic PageInterceptor pageInterceptor() {PageInterceptor pageInterceptor = new PageInterceptor();Properties props = new Properties();props.setProperty("helperDialect", "mysql");pageInterceptor.setProperties(props);return pageInterceptor;}
}
Service 层使用
@Service
public class UserService {@Autowiredprivate UserMapper userMapper;public PageInfo<User> getUsersByPage(PageParam param) {PageHelper.startPage(param.getPageNum(), param.getPageSize());List<User> users = userMapper.selectAll(); // 无需手动拼接分页参数return new PageInfo<>(users);}
}
Mapper 接口
@Mapper
public interface UserMapper {@Select("SELECT * FROM user")List<User> selectAll();
}

方案 3:MyBatis-Plus 分页插件(推荐)

配置插件
@Configuration
public class MyBatisPlusConfig {@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));return interceptor;}
}
Service 层使用
@Service
public class UserService {@Autowiredprivate UserMapper userMapper;public Page<User> getUsersByPage(PageParam param) {Page<User> page = new Page<>(param.getPageNum(), param.getPageSize());return userMapper.selectPage(page, null);}
}
Mapper 接口
public interface UserMapper extends BaseMapper<User> {// 继承 BaseMapper 自动获得分页能力
}

三、游标分页实现(手动分页)

1. 请求参数类

public class CursorParam {private Integer pageSize = 10;    // 每页数量private Long lastCursor;         // 上一页最后记录的游标值// Getter & Setter
}

2. 响应参数类

public class CursorResult<T> {private List<T> list;       // 当前页数据private Boolean hasNext;    // 是否有下一页private Long nextCursor;    // 下一页起始游标// Getter & Setter
}

3. Mapper 接口

@Mapper
public interface UserMapper {List<User> selectByCursor(@Param("cursor") Long cursor, @Param("pageSize") Integer pageSize);
}

4. XML 映射文件

<select id="selectByCursor" resultType="User">SELECT * FROM user<where><if test="cursor != null">id &lt; #{cursor}</if></where>ORDER BY id DESCLIMIT #{pageSize}
</select>

5. Service 层逻辑

@Service
public class UserService {@Autowiredprivate UserMapper userMapper;public CursorResult<User> getUsersByCursor(CursorParam param) {// 实际查询 pageSize + 1 条用于判断是否有下一页List<User> users = userMapper.selectByCursor(param.getLastCursor(), param.getPageSize() + 1);CursorResult<User> result = new CursorResult<>();boolean hasNext = users.size() > param.getPageSize();if (hasNext) {result.setList(users.subList(0, param.getPageSize()));result.setNextCursor(users.get(param.getPageSize() - 1).getId());} else {result.setList(users);result.setNextCursor(null);}result.setHasNext(hasNext);return result;}
}

四、分页插件对比

插件/方案优点缺点适用场景
手动分页完全控制 SQL代码冗余,维护成本高简单项目、特殊分页需求
PageHelper零侵入,简单易用依赖特定语法(PageHelper.startPage()传统 MyBatis 项目
MyBatis-Plus深度整合,支持 Lambda 表达式需继承 BaseMapperMyBatis-Plus 项目
游标分页高性能,无 OFFSET无法跳页移动端列表、实时数据流

五、注意事项

  1. 索引优化:确保排序字段(如 id)有索引
  2. 安全限制:限制最大 pageSize(建议 ≤ 100)
  3. 数据一致性:分页期间数据变化可能导致结果差异
  4. 参数校验:校验 pageNum ≥1,pageSize ≥1

六、扩展建议

  1. 统一分页响应格式
public class R<T> {private Integer code;private String msg;private T data;private PageInfo page; // 分页元数据
}
  1. 动态排序支持
<select id="selectByPage" resultType="User">SELECT * FROM userORDER BY ${orderBy} ${orderDir}LIMIT #{offset}, #{pageSize}
</select>
  1. Redis 缓存优化
// 缓存总记录数
public Long getTotalCount() {String cacheKey = "user:total";Long total = redisTemplate.opsForValue().get(cacheKey);if (total == null) {total = userMapper.selectTotalCount();redisTemplate.opsForValue().set(cacheKey, total, 5, TimeUnit.MINUTES);}return total;
}
http://www.xdnf.cn/news/660367.html

相关文章:

  • 【排错】kylinLinx环境python读json文件报错UTF-8 BOM
  • WEB安全--RCE--webshell HIDS bypass3
  • try-with-resources
  • md650场景联动
  • 华为OD机试真题——考勤信息(2025A卷:100分)Java/python/JavaScript/C/C++/GO最佳实现
  • Go语言入门指南
  • lwip_bind、lwip_listen 是阻塞函数吗
  • 从实训到实战:家庭教育干预课程的产教融合定制方案
  • 1期临床试验中的联合i3+3设计
  • IndexTTS - B 站推出的文本转语音模型,支持拼音纠正汉字发音(附整合包)
  • 基于web的二手交易商城-设计
  • uniapp好不好
  • 攻防世界 unseping
  • 从0到1搭建AI绘画模型:Stable Diffusion微调全流程避坑指南
  • 企业网站架构部署与优化-Nginx性能调优与深度监控
  • 系统分析师-考后总结
  • 凯恩斯宏观经济学与马歇尔微观经济学的数学建模和形式化表征
  • 【C++11】lambda表达式 || 函数包装器 || bind用法
  • 论文返修时/录用后,能变更作者、增加或减少作者吗?
  • ros2-moveit2 配置与执行自定义urdf的报错处理
  • 基于私有化 DeepSeek 大模型的工业罐区跑冒滴漏检测技术研究与应用
  • Rust 项目实战:命令行搜索工具 grep
  • 1-600MW 燃气轮机市场未来展望:低碳技术、氢能转型与智能化运维发展趋势报告
  • PSDA安装配置
  • 因重新安装python新版本,pycharm提示找不到python.exe(No Python at“c:\python.exe“)问题解决方法
  • 【虚拟仪器技术】期末7个LABVIEW仿真实验
  • 【TVM 教程】开发环境中加入 microTVM
  • 11 接口自动化-框架封装之统一请求封装和接口关联封装
  • 日志采集 Agent 性能大比拼——LoongCollector 性能深度测评
  • win11+vs2022 安装opencv 4.11.0图解教程