大数跨境
0
0

MyBatis + PageHelper 分页内幕,PageHelper 是如何动态构建 LIMIT 分页 SQL 的

MyBatis + PageHelper 分页内幕,PageHelper 是如何动态构建 LIMIT 分页 SQL 的 乡下小哥编程
2025-11-26
0
导读:若依系统中有很多的表格数据、后端接口实体中并没有看到有接收分页大小和页码的属性,在系统的mapper层面SQL语句中并没有看到对应的分页限制。它是如何实现这个分页效果的呢?是将所有的数据查询出来,然后
前言
      若依系统中有很多的表格数据、后端接口实体中并没有看到有接收分页大小和页码的属性,在系统的mapper层面SQL语句中并没有看到对应的分页限制。它是如何实现这个分页效果的呢?是将所有的数据查询出来,然后再分页吗?这样效率岂不是非常低。带着这样的疑问,稍微研究了一下这个系统的分页方法。
先看效果
      若依系统中提供了很多类似这样的表格,然后每个表格都用到了分页,这个分页做的还是不错的。简单大方,易于操作。
分析说明
     首先看后端接口,该系统中的列表查询大多是如下方式。用一个实体接收参数(这个实体不包含页码和大小)。然后方法执行的时候都会先执行 startPage(),紧接着是查询列表对象、最后是 将查询的数据封装到表格数据中。
@GetMapping("/list")public TableDataInfo list(SysCoupon sysCoupon){  startPage();  List<SysCoupon> list = sysCouponService.selectSysCouponList(sysCoupon);  return getDataTable(list);}
      首先看一下startPage();这个方法干了什么事情,实际上执行的是下方这个方法。我们可以看到这里有从实体中获取 分页的页码 和 页码的大小(先只考虑这两个参数,其它的暂不考虑)。这两个参数来自 PageDomain 这个对象,而 TableSupport.buildPageRequest() 方法调用返回的是它。
 /** * 设置请求分页数据 */public static void startPage(){  PageDomain pageDomain = TableSupport.buildPageRequest();  Integer pageNum = pageDomain.getPageNum();  Integer pageSize = pageDomain.getPageSize();  String orderBy = SqlUtil.escapeOrderBySql(pageDomain.getOrderBy());  Boolean reasonable = pageDomain.getReasonable();  PageHelper.startPage(pageNum, pageSize, orderBy).setReasonable(reasonable);}
     具体看一下 TableSupport.buildPageRequest() 这个方法干了什么事情,这个方法实际上做的事情就是组装这个分页对象。这里可以看到有 如何获取分页的页码和大小,通过调用 ServletUtils.getParameter(参数) 这个方法来获取相关参数
/** * 封装分页对象 */public static PageDomain getPageDomain(){  PageDomain pageDomain = new PageDomain();  pageDomain.setPageNum(Convert.toInt(ServletUtils.getParameter(PAGE_NUM), 1));  pageDomain.setPageSize(Convert.toInt(ServletUtils.getParameter(PAGE_SIZE), 10));  pageDomain.setOrderByColumn(ServletUtils.getParameter(ORDER_BY_COLUMN));  pageDomain.setIsAsc(ServletUtils.getParameter(IS_ASC));  pageDomain.setReasonable(ServletUtils.getParameterToBool(REASONABLE));  return pageDomain;}

      这个方法的作用简单来说就是:从请求参数中获取指定名称的值。例如前端调用URL 查询字符串(GET 请求):/list?pageNum=1&pageSize=10 ,则可以通过调用此方法获取到相关的页码和大小。这样就可以说的通了,及时接收的实体不包含这两个参数,也可以接收到分页相关的参数。

public static String getParameter(String name){  return getRequest().getParameter(name);}
      现在获取到了分页的页码和大小,并没有在  xxxMapper.xml 文件中的SQL查询条件看到分页的限制。难道是在执行代码的时候,动态追加的吗?答案确实如此。在  startPage() 方法中的结尾 有 PageHelper.startPage(pageNum, pageSize, orderBy).setReasonable(reasonable);
    调用 startPage() 后,会创建一个 Page 对象,存放在 ThreadLocal 中。这样 同一个线程后续执行的 Mapper 查询 可以读取到这个分页信息。以下截图可以看出的确如此是这样的。
动态构建简单说明(这里牵涉到分页插件的调用,不详细赘述)

     当 Mapper 查询执行时,MyBatis 会经过 PageInterceptor.intercept();拦截器会从ThreadLocal 取出分页信息;调用 Dialect 的getPageSql()生成带分页限制的 SQL。

看一下前端参数的传递及请求数据

// 查询参数 queryParams: {pageNum: 1,pageSize: 10,userName: null,productName: null,orderId: null,commentScore: null,},

调用接口

export function listComment(query) {  return request({    url'/system/comment/list',    method'get',    params: query  })}


总结

      后端是通过从 HttpServletRequest 对象中获取分页的页码和大小,然后创建Page分页对象,在执行sql查询的时候,动态构建分页限制条件。从而实现分页,并不是在查询到所有数据之后再进行的分页处理。

调用链路说明

前端(Vue/Element)       |       | 1.GET /list?pageNum=1&pageSize=10       v后端 Controller       |       | 2.startPage()       vService → MyBatis Mapper       |       | 3.SQL 执行触发分页拦截器 PageInterceptor       vPageInterceptor(核心动态 SQL 构建)       |       | 4.生成 count(*) 查询       | 5.生成 分页 SQL(LIMIT / OFFSET       v数据库       |       v拦截器封装 PageInfo       |       vController 返回 JSON(包含 rows / total)       |       v前端表格渲染分页


公众号回复如下内容即可获取对应资料

数字 1、表示获取后端开发学习资料

数字 2、表示获取前端开发学习资料

数字 3、表示获取 软考中级设计师资料

数字 4、表示获取后端常用在线免费学习资料

数字 5、计算机毕业设计优秀论文模板

数字 6、前后端分离项目案例源码

数字 7、SSM项目案例源码

数字 8、视频轮播图全部案例源码

数字 9、商城系统项目 前后端分离源码

数字10:商城系统源码 前后端分离项目【毕业设计系统】

数字11:毕业论文答辩PPT模板案例

数字12:系统部署+论文指导+开题指导+开发指导

数字13:万字答辩问题汇总文档

数字14:前后端项目结构模块详细讲解说明(小白可懂)

数字15:协同过滤算法+敏感词过滤+SpringBoot+Vue 商城源码

数字16:微信小程序考勤打卡 系统源码

更多。。。

【声明】内容源于网络
0
0
乡下小哥编程
主要用公众号分享纯干货知识、日常开发经验总结、前沿技术、优秀项目源码案例等
内容 246
粉丝 0
乡下小哥编程 主要用公众号分享纯干货知识、日常开发经验总结、前沿技术、优秀项目源码案例等
总阅读13
粉丝0
内容246