若伊项目学习 后端分页源码分析

0.开篇引入

数据分页是一个常见的需求,但是优雅的实现是一门学问。

开发要点:

  • 使用分页的成本足够低,实现分页的代码要尽可能简单。
  • 足够的通用性,新的模块可以快捷使用,在方法内部自由的控制分页

看看若依是怎么实现的吧。

若依的orm框架是mybatis,采用的分页插件是PageHelper

我们从最外层看,层层深入。

1.controller层

Java 复制代码
@RestController
@RequestMapping("/system/dict/data")
// 继承了BaseController,分页的一些功能是在里面实现,作为controller层的基类
public class SysDictDataController extends BaseController
{
    @Autowired
    private ISysDictDataService dictDataService;

    @PreAuthorize("@ss.hasPermi('system:dict:list')")
    @GetMapping("/list")
    public TableDataInfo list(SysDictData dictData)
    {
        // 作为标识,开始分页,通知下一个方法需要分页
        // startPage本身的方法是存在BaseController中的可以直接使用
        startPage();
        List<SysDictData> list = dictDataService.selectDictDataList(dictData);
        return getDataTable(list);
    }

其中startPage和getDataTable的方法是在BaseController中,说明分页的功能是由BaseController里面实现。

controller层只要继承了这个类,就可以实现分页的这个功能了。

2.BaseController类和PageUtils类和TableSupport类分析

先整体过一遍BaseController的方法,然后具体的看方法里面是怎么实现的。

Java 复制代码
/**
 * web层通用数据处理
 * 
 * @author ruoyi
 */
public class BaseController
{
    //日志相关
    protected final Logger logger = LoggerFactory.getLogger(this.getClass());
    
    /**
     * 设置请求分页数据
     */
    protected void startPage()
    {
        PageUtils.startPage();
    }
    
    /**
     * 设置请求排序数据
     */
    protected void startOrderBy()
    {
        PageDomain pageDomain = TableSupport.buildPageRequest();
        if (StringUtils.isNotEmpty(pageDomain.getOrderBy()))
        {
            String orderBy = SqlUtil.escapeOrderBySql(pageDomain.getOrderBy());
            PageHelper.orderBy(orderBy);
        }
    }

    /**
     * 清理分页的线程变量
     */
    protected void clearPage()
    {
        PageUtils.clearPage();
    }

    /**
     * 响应请求分页数据
     */
    @SuppressWarnings({ "rawtypes", "unchecked" })
    protected TableDataInfo getDataTable(List<?> list)
    {
        TableDataInfo rspData = new TableDataInfo();
        rspData.setCode(HttpStatus.SUCCESS);
        rspData.setMsg("查询成功");
        rspData.setRows(list);
        rspData.setTotal(new PageInfo(list).getTotal());
        return rspData;
    }

好,整体的过了一遍,然后看具体的方法

2.1 分页入口

Java 复制代码
// BaseController.java
protected void startPage() { 
    PageUtils.startPage(); 
}
Java 复制代码
// PageUtils.java
public static void startPage()
{
    // 这里用到了TableSupport这个类去构建初始的PageDomain,本质上就是对http的请求参数获取封装成一个对象
    PageDomain pageDomain = TableSupport.buildPageRequest();
    Integer pageNum = pageDomain.getPageNum();
    Integer pageSize = pageDomain.getPageSize();
    // 这里是对orderby参数做的一些校验,方式sql注入,无其他意义。
    String orderBy = SqlUtil.escapeOrderBySql(pageDomain.getOrderBy());
    // 这里的参数是是否设置响应,是个boolean参数默认为true
    Boolean reasonable = pageDomain.getReasonable();
    // 封装PageHelper对象,把请求参数全部放进去。
    PageHelper.startPage(pageNum, pageSize, orderBy).setReasonable(reasonable);
}
Java 复制代码
// TableSupport.java
/**
 * 表格数据处理
 */
public class TableSupport
{
    /**
     * 当前记录起始索引
     */
    public static final String PAGE_NUM = "pageNum";

    /**
     * 每页显示记录数
     */
    public static final String PAGE_SIZE = "pageSize";

    /**
     * 排序列
     */
    public static final String ORDER_BY_COLUMN = "orderByColumn";

    /**
     * 排序的方向 "desc" 或者 "asc".
     */
    public static final String IS_ASC = "isAsc";

    /**
     * 分页参数合理化
     */
    public static final String REASONABLE = "reasonable";

    /**
     * 封装分页对象
     * 本质上就是获取http的请求参数封装成一个对象
     */
    public static PageDomain getPageDomain()
    {
        PageDomain pageDomain = new PageDomain();
        // 从http中获取pageNum的参数
        pageDomain.setPageNum(Convert.toInt(ServletUtils.getParameter(PAGE_NUM), 1));
        // 从http中获取pageSize的参数
        pageDomain.setPageSize(Convert.toInt(ServletUtils.getParameter(PAGE_SIZE), 10));
        // 从http中获取...
        pageDomain.setOrderByColumn(ServletUtils.getParameter(ORDER_BY_COLUMN));
        pageDomain.setIsAsc(ServletUtils.getParameter(IS_ASC));
        pageDomain.setReasonable(ServletUtils.getParameterToBool(REASONABLE));
        return pageDomain;
    }

    public static PageDomain buildPageRequest()
    {
        return getPageDomain();
    }
}

2.2分页结果返回封装

Java 复制代码
// SysDictDataController.java
public TableDataInfo list(SysDictData dictData)
{
    startPage();
    List<SysDictData> list = dictDataService.selectDictDataList(dictData);
    // 通过getDataTable封装返回分页的结果
    return getDataTable(list);
}
Java 复制代码
// BaseController.java
protected TableDataInfo getDataTable(List<?> list)
{
    // 创建分页返回的对象
    TableDataInfo rspData = new TableDataInfo();
    rspData.setCode(HttpStatus.SUCCESS);
    rspData.setMsg("查询成功");
    // 存放结果list
    rspData.setRows(list);
    //这里会先检测是否能转为page对象,可以转的话就返回总数,要不然就是list的大小
    rspData.setTotal(new PageInfo(list).getTotal());
    return rspData;
}
//  获取总数的核心逻辑
if (list instanceof Page) {
    Page page = (Page)list;
    this.pageNum = page.getPageNum();
    this.pageSize = page.getPageSize();
    this.pages = page.getPages();
    this.size = page.size();
    if (this.size == 0) {
        this.startRow = 0L;
        this.endRow = 0L;
    } else {
        this.startRow = page.getStartRow() + 1L;
        this.endRow = this.startRow - 1L + (long)this.size;
    }
相关推荐
excel2 小时前
Nginx 与 Node.js(PM2)的对比优势及 HTTPS 自动续签配置详解
后端
bobz9654 小时前
vxlan 为什么一定要封装在 udp 报文里?
后端
bobz9654 小时前
vxlan 直接使用 ip 层封装是否可以?
后端
郑道6 小时前
Docker 在 macOS 下的安装与 Gitea 部署经验总结
后端
3Katrina6 小时前
妈妈再也不用担心我的课设了---Vibe Coding帮你实现期末课设!
前端·后端·设计
汪子熙6 小时前
HSQLDB 数据库锁获取失败深度解析
数据库·后端
没逻辑7 小时前
主流消息队列模型与选型对比(RabbitMQ / Kafka / RocketMQ)
后端·消息队列
倚栏听风雨7 小时前
SwingUtilities.invokeLater 详解
后端
Java中文社群7 小时前
AI实战:一键生成数字人视频!
java·人工智能·后端