RuoYi-Vue-Plus数据分页:对象化分页插件

RuoYi-Vue-Plus数据分页:对象化分页插件

【免费下载链接】RuoYi-Vue-Plus 多租户后台管理系统 重写RuoYi-Vue所有功能 集成 Sa-Token、Mybatis-Plus、Warm-Flow工作流、SpringDoc、Hutool、OSS 定期同步 【免费下载链接】RuoYi-Vue-Plus 项目地址: https://gitcode.com/dromara/RuoYi-Vue-Plus

前言:分页的痛点与解决方案

在传统Web应用开发中,数据分页是一个常见但繁琐的需求。开发者往往需要:

  • 手动计算分页参数(当前页码、每页大小)
  • 处理SQL的LIMIT和OFFSET语句
  • 构建复杂的分页响应结构
  • 维护排序逻辑
  • 处理边界条件和异常情况

RuoYi-Vue-Plus通过对象化分页插件完美解决了这些问题,提供了统一、简洁、强大的分页解决方案。

核心分页组件解析

PageQuery:统一的分页查询对象

PageQuery 类是分页功能的核心,它封装了所有分页相关的参数:

@Data
public class PageQuery implements Serializable {
    // 分页大小
    private Integer pageSize;
    
    // 当前页数
    private Integer pageNum;
    
    // 排序列
    private String orderByColumn;
    
    // 排序方向(desc/asc)
    private String isAsc;
    
    // 默认值常量
    public static final int DEFAULT_PAGE_NUM = 1;
    public static final int DEFAULT_PAGE_SIZE = Integer.MAX_VALUE;
    
    // 构建MyBatis-Plus分页对象
    public <T> Page<T> build() {
        // 参数校验和默认值处理
        Integer pageNum = ObjectUtil.defaultIfNull(getPageNum(), DEFAULT_PAGE_NUM);
        Integer pageSize = ObjectUtil.defaultIfNull(getPageSize(), DEFAULT_PAGE_SIZE);
        
        Page<T> page = new Page<>(pageNum, pageSize);
        List<OrderItem> orderItems = buildOrderItem();
        if (CollUtil.isNotEmpty(orderItems)) {
            page.addOrder(orderItems);
        }
        return page;
    }
}

TableDataInfo:标准的分页响应结构

TableDataInfo 提供了统一的分页响应格式:

@Data
@NoArgsConstructor
public class TableDataInfo<T> implements Serializable {
    // 总记录数
    private long total;
    
    // 列表数据
    private List<T> rows;
    
    // 状态码
    private int code;
    
    // 消息内容
    private String msg;
    
    // 多种构建方式
    public static <T> TableDataInfo<T> build(IPage<T> page) {
        TableDataInfo<T> rspData = new TableDataInfo<>();
        rspData.setCode(HttpStatus.HTTP_OK);
        rspData.setMsg("查询成功");
        rspData.setRows(page.getRecords());
        rspData.setTotal(page.getTotal());
        return rspData;
    }
}

分页功能使用指南

1. 控制器层使用示例

在Controller中,分页使用变得极其简单:

@RestController
@RequestMapping("/system/user")
public class SysUserController extends BaseController {

    @GetMapping("/list")
    public TableDataInfo<SysUserVo> list(SysUserBo user, PageQuery pageQuery) {
        return userService.selectPageUserList(user, pageQuery);
    }
}

2. 服务层实现

服务层接收PageQuery对象并构建分页查询:

@Service
public class SysUserServiceImpl implements ISysUserService {

    @Override
    public TableDataInfo<SysUserVo> selectPageUserList(SysUserBo user, PageQuery pageQuery) {
        // 构建MyBatis-Plus分页对象
        Page<SysUserVo> page = pageQuery.build();
        
        // 执行分页查询
        Page<SysUserVo> result = userMapper.selectPageUserList(page, user);
        
        // 返回标准分页响应
        return TableDataInfo.build(result);
    }
}

3. 数据访问层

Mapper层使用MyBatis-Plus的分页插件:

@Mapper
public interface SysUserMapper extends BaseMapper<SysUser> {

    Page<SysUserVo> selectPageUserList(
        @Param("page") Page<SysUserVo> page, 
        @Param("bo") SysUserBo bo
    );
}

对应的XML映射文件:

<select id="selectPageUserList" resultType="org.dromara.system.vo.SysUserVo">
    SELECT 
        u.user_id, u.user_name, u.nick_name, u.dept_id,
        d.dept_name, u.email, u.phonenumber, u.status,
        u.create_time
    FROM sys_user u
    LEFT JOIN sys_dept d ON u.dept_id = d.dept_id
    <where>
        u.del_flag = '0'
        <if test="bo.userName != null and bo.userName != ''">
            AND u.user_name LIKE CONCAT('%', #{bo.userName}, '%')
        </if>
        <if test="bo.status != null and bo.status != ''">
            AND u.status = #{bo.status}
        </if>
        <!-- 更多查询条件 -->
    </where>
</select>

高级特性详解

1. 智能排序支持

PageQuery支持多种排序格式:

// 单字段排序
{isAsc:"asc", orderByColumn:"id"} 
// → ORDER BY id ASC

// 多字段统一排序
{isAsc:"desc", orderByColumn:"id,createTime"} 
// → ORDER BY id DESC, create_time DESC

// 多字段分别排序
{isAsc:"asc,desc", orderByColumn:"id,createTime"} 
// → ORDER BY id ASC, create_time DESC

2. 前端排序兼容

自动兼容前端常见的排序类型:

// 自动转换前端排序标识
isAsc = StringUtils.replaceEach(isAsc, 
    new String[]{"ascending", "descending"}, 
    new String[]{"asc", "desc"}
);

3. SQL注入防护

内置SQL注入防护机制:

// 对排序字段进行安全过滤
String orderBy = SqlUtil.escapeOrderBySql(orderByColumn);
orderBy = StringUtils.toUnderScoreCase(orderBy); // 转下划线命名

4. 假分页支持

对于内存中的数据,也提供分页支持:

public static <T> TableDataInfo<T> build(List<T> list, IPage<T> page) {
    if (CollUtil.isEmpty(list)) {
        return TableDataInfo.build();
    }
    // 使用Hutool进行内存分页
    List<T> pageList = CollUtil.page(
        (int) page.getCurrent() - 1, 
        (int) page.getSize(), 
        list
    );
    return new TableDataInfo<>(pageList, list.size());
}

最佳实践指南

1. 统一的分页参数命名

建议前端使用统一的参数名:

  • pageNum: 当前页码
  • pageSize: 每页大小
  • orderByColumn: 排序字段
  • isAsc: 排序方向

2. 默认值处理策略

// 推荐的处理方式
public TableDataInfo<SysUserVo> list(
    @RequestParam(defaultValue = "1") Integer pageNum,
    @RequestParam(defaultValue = "10") Integer pageSize
) {
    PageQuery pageQuery = new PageQuery(pageSize, pageNum);
    // ... 业务逻辑
}

3. 异常处理

try {
    Page<SysUserVo> page = pageQuery.build();
    // 执行查询
} catch (ServiceException e) {
    // 处理排序参数错误等异常
    return TableDataInfo.build().setCode(500).setMsg(e.getMessage());
}

性能优化建议

1. 分页大小限制

// 限制最大分页大小,防止恶意请求
private static final int MAX_PAGE_SIZE = 1000;

public <T> Page<T> build() {
    Integer pageSize = ObjectUtil.defaultIfNull(getPageSize(), DEFAULT_PAGE_SIZE);
    pageSize = Math.min(pageSize, MAX_PAGE_SIZE);
    // ... 其他逻辑
}

2. 索引优化

确保排序字段和查询条件字段都有合适的索引:

-- 为用户表的常用查询字段创建索引
CREATE INDEX idx_user_status ON sys_user(status);
CREATE INDEX idx_user_dept ON sys_user(dept_id);
CREATE INDEX idx_user_name ON sys_user(user_name);

与其他组件的集成

1. 与Sa-Token权限集成

@SaCheckPermission("system:user:list")
@GetMapping("/list")
public TableDataInfo<SysUserVo> list(SysUserBo user, PageQuery pageQuery) {
    // 权限校验通过后执行分页查询
    return userService.selectPageUserList(user, pageQuery);
}

2. 与多租户集成

public TableDataInfo<SysUserVo> selectPageUserList(SysUserBo user, PageQuery pageQuery) {
    Page<SysUserVo> page = pageQuery.build();
    
    // 自动添加租户过滤条件
    TenantHelper.execute(() -> {
        Page<SysUserVo> result = userMapper.selectPageUserList(page, user);
        return TableDataInfo.build(result);
    });
}

常见问题解决方案

1. 分页参数传递问题

问题:前端传递的分页参数无法正确解析 解决方案:确保使用正确的参数名和格式

// 前端请求示例
axios.get('/system/user/list', {
    params: {
        pageNum: 1,
        pageSize: 10,
        orderByColumn: 'createTime',
        isAsc: 'desc'
    }
});

2. 排序字段映射问题

问题:前端字段名与数据库字段名不一致 解决方案:使用字段映射或自定义转换逻辑

// 自定义字段映射
private String mapOrderByColumn(String column) {
    switch (column) {
        case "createTime": return "create_time";
        case "userName": return "user_name";
        default: return column;
    }
}

总结

RuoYi-Vue-Plus的对象化分页插件通过PageQueryTableDataInfo两个核心类,提供了完整的分页解决方案:

  • 统一规范:标准化的参数传递和响应格式
  • 智能排序:支持多种排序方式和自动转换
  • 安全防护:内置SQL注入防护机制
  • 灵活扩展:支持真分页和假分页场景
  • 生态集成:与权限、多租户等组件无缝集成

通过这套分页方案,开发者可以专注于业务逻辑的实现,而无需关心分页的底层细节,大大提高了开发效率和代码质量。

【免费下载链接】RuoYi-Vue-Plus 多租户后台管理系统 重写RuoYi-Vue所有功能 集成 Sa-Token、Mybatis-Plus、Warm-Flow工作流、SpringDoc、Hutool、OSS 定期同步 【免费下载链接】RuoYi-Vue-Plus 项目地址: https://gitcode.com/dromara/RuoYi-Vue-Plus

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值