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

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;
    }
相关推荐
码事漫谈1 小时前
我花了一周时间,看AI公司怎么"管"AI,结果越看越后背发凉
后端
真上帝的左手2 小时前
30. 技术专题-‌应用框架组件-分布式架构
分布式·架构·系统架构·分布式架构
爱学习的程序媛4 小时前
Python常用框架梳理
开发语言·后端·python·ai·web·后端框架·python框架
GISer_Jing4 小时前
Agent核心内容
后端·ai·langchain
budingxiaomoli5 小时前
Spring统一功能处理
java·后端·spring
Decay__5 小时前
SQLServer 索引与 SQL 优化 —— 聚集索引、非聚集索引、覆盖索引、碎片维护与执行计划
运维·数据库·sql·mysql·架构
某鹏6 小时前
网关选型:Go、Java、Graal 谁都不能完胜
后端
用户0247836530976 小时前
从屎山到防线:14 行状态迁移表,给任务系统装上防瞎跳闸门
后端
CQU_JIAKE6 小时前
11.13[体系结构】指标,五级流水线,八级流水线,虚实地址思考
后端
Dawson Zhu7 小时前
【分布式训练④】张量并行与序列并行:把单层参数切碎分到多张卡
人工智能·语言模型·架构·aigc·agi