分页处理 - 若依cloud -【 129 】

文章详细介绍了前端使用Element-UI的封装分页组件和后端通过mybatis的pageHelper实现分页的过程,包括组件封装、全局注册、参数传递和注意事项,特别是针对分页的一些常见问题进行了分析。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

129 分页功能实现详解 | RuoYi

提示:

        前后端分页实现流程

一 前端

1 element-ui提供了el-pagination,可以直接去使用,是没问题的。

2 只是说项目里也封装了一个pagination组件,src\components\Pagination\index.vue。封装后的pagination组件兼容el-pagination,即el-pagination的所有属性、事件等等都会被支持。

<template>
  <div :class="{'hidden':hidden}" class="pagination-container">
    <el-pagination
      :background="background"
      :current-page.sync="currentPage"
      :page-size.sync="pageSize"
      :layout="layout"
      :page-sizes="pageSizes"
      :pager-count="pagerCount"
      :total="total"
      v-bind="$attrs"
      @size-change="handleSizeChange"
      @current-change="handleCurrentChange"
    />
  </div>
</template>

<script>
import { scrollTo } from '@/utils/scroll-to'

export default {
  name: 'Pagination',
  props: {
    total: {
      required: true,
      type: Number
    },
    page: {
      type: Number,
      default: 1
    },
    limit: {
      type: Number,
      default: 20
    },
    pageSizes: {
      type: Array,
      // 区别于el-pagination,一些默认值做了一些简单修改
      default() {
        return [10, 20, 30, 50]
      }
    },
    // 移动端页码按钮的数量,默认值5。
    // 如果非移动端则是7
    pagerCount: {
      type: Number,
      default: document.body.clientWidth < 992 ? 5 : 7
    },
    layout: {
      type: String,
      default: 'total, sizes, prev, pager, next, jumper'
    },
    background: {
      type: Boolean,
      default: true
    },
    autoScroll: {
      type: Boolean,
      default: true
    },
    hidden: {
      type: Boolean,
      default: false
    }
  },
  data() {
    return {
    };
  },
  computed: {
    currentPage: {
      get() {
        return this.page
      },
      set(val) {
        this.$emit('update:page', val)
      }
    },
    pageSize: {
      get() {
        return this.limit
      },
      set(val) {
        this.$emit('update:limit', val)
      }
    }
  },
  methods: {
    handleSizeChange(val) {
      if (this.currentPage * val > this.total) {
        this.currentPage = 1
      }
      this.$emit('pagination', { page: this.currentPage, limit: val })
      if (this.autoScroll) {
        // 点击下一页后,自动滚动到第1条数据所在的位置
        scrollTo(0, 800)
      }
    },
    handleCurrentChange(val) {
      this.$emit('pagination', { page: val, limit: this.pageSize })
      if (this.autoScroll) {
        scrollTo(0, 800)
      }
    }
  }
}
</script>

<style scoped>
.pagination-container {
  background: #fff;
  padding: 32px 16px;
}
.pagination-container.hidden {
  display: none;
}
</style>

3 全局注册封装的pagination组件(在前端任何页面都可以使用):main.js

// 全局组件挂载
Vue.component('Pagination', Pagination)
// 分页组件
import Pagination from "@/components/Pagination";

4 封装的pagination组件的使用案例:src\views\system\role\selectUser.vue

5 前端调用实现:前端定义分页流程

// 一般在查询参数中定义分页变量
queryParams: {
  pageNum: 1,
  pageSize: 10
},

// 页面添加分页组件,传入分页变量
<pagination
  v-show="total>0"
  :total="total"
  :page.sync="queryParams.pageNum"
  :limit.sync="queryParams.pageSize"
  @pagination="getList"
/>

// 声明分页参数和查询参数
export default {
  data() {
    return {
      // 查询参数
      queryParams: {
        pageNum: 1,            // 第1页
        pageSize: 10,          // 每页10条
        roleId: undefined,     // 查询条件1:roleId,角色id
        userName: undefined,   // 查询条件2:用户名称 
        phonenumber: undefined // 查询条件3:电话号码
      }
    };
  }
}

// 调用后台方法,传入参数 获取结果
// queryParams是分页参数和查询参数
listUser(this.queryParams).then(response => {
    this.userList = response.rows;
    this.total = response.total;
  }
);

​​​​6 扩展:一般在项目里直接用封装的分页组件就已经足够满足了。如果不满足的话,可以再参考el-pagination去定制一些属性加进来。或者是你直接用饿了么的也可以啊,都是可以的。 

二 后端

1 ruoyi-common-core#TableDataInfo:表格分页数据对象,其中rows就是表格数据

public class TableDataInfo implements Serializable
{
    private static final long serialVersionUID = 1L;

    /** 总记录数 */
    private long total;

    /** 列表数据 */
    private List<?> rows;

    /** 消息状态码 */
    private int code;

    /** 消息内容 */
    private String msg;
}

2 ruoyi-common-core#PageUtils:解决实体类(如下面的SysPost sysPost)中都没有pageSize和pageNumber属性接收前端参数

/**
 * 分页工具类
 * 
 * @author ruoyi
 */
public class PageUtils extends PageHelper
{
    /**
     * 设置请求分页数据:
     *      就是从request中把前端传过来的几个参数拿过来。
     *      然后设置到pageDomain里面。
     */
    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);
    }
}

3 ruoyi-system#SysPostController#list

    /**
     * 获取岗位列表
     */
    @RequiresPermissions("system:post:list")
    @GetMapping("/list")
    public TableDataInfo list(SysPost post)
    {
        // 分页
        startPage();
        // 当前页数据
        List<SysPost> list = postService.selectPostList(post);
        // 返回给前端
        return getDataTable(list);
    }

4 【pagehelper框架】PageMethod#startPage:分页

    // 当前请求
    protected static final ThreadLocal<Page> LOCAL_PAGE = new ThreadLocal();

    // 即在当前请求下面,去给我们进行一个分页
    // 会自动在sql语句中拼接pageNum、pageSize、orderBy这3个参数的limit分页语句
    // 简化代码:不用在SysPostMapper.xml中修改sql语句
    public static <E> Page<E> startPage(int pageNum, int pageSize, String orderBy) {
        Page<E> page = startPage(pageNum, pageSize);
        page.setOrderBy(orderBy);
        return page;
    }

5 扩展:如果不想分页,就把startPage();这句代码删掉就行了。

三 注意事项(坑)

1 常见坑点1:selectPostById莫名其妙的分页。例如下面这段代码

startPage();
List<User> list;
if(user != null){
    list = userService.selectUserList(user);
} else {
    list = new ArrayList<User>();
}
// 当user == null时,分页会消费在此查询上。
// 本来是查询单个的,但结果却莫名其妙给你分了个页
Post post = postService.selectPostById(1L);
return getDataTable(list);

原因分析:这种情况下由于user存在null的情况,就会导致pageHelper生产了一个分页参数,但是没有被消费,这个参数就会一直保留在这个线程上。 当这个线程再次被使用时,就可能导致不该分页的方法去消费这个分页参数,这就产生了莫名其妙的分页。
上面这个代码,应该写成下面这个样子才能保证安全。

        就是说你使用了这个startPage,你又不去分页啊,这个地方又没有分页。那么的话,你下一次查询就是下一个查询,它就会带上这个分页,所以的话呢就会莫名其妙。这个你本来不需要分页的,反而被分页了,所以的话就需要注意一下。

List<User> list;
if(user != null){
	startPage();
	list = userService.selectUserList(user);
} else {
	list = new ArrayList<User>();
}
Post post = postService.selectPostById(1L);
return getDataTable(list);

2 常见坑点2:添加了startPage方法。也没有正常分页。例如下面这段代码

startPage();
// 错:分页错误地加上这里
Post post = postService.selectPostById(1L);
List<User> list = userService.selectUserList(user);
return getDataTable(list);

原因分析:只对该语句以后的第一个查询(Select)语句得到的数据进行分页。
上面这个代码,应该写成下面这个样子才能正常分页。

        因为的话,你下面的话必须是你分页的那个sql语句的一个查询方法(XxxMapper.xml)。

Post post = postService.selectPostById(1L);
startPage();
List<User> list = userService.selectUserList(user);
return getDataTable(list);

3 提示

        项目分页插件默认是Mysql语法,如果项目改为其他数据库需修改配置application.yml文件中的属性helperDialect: 你的数据库

4 注意

        只要你可以保证在PageHelper方法调用后紧跟MyBatis查询方法,这就是安全的。因为PageHelperfinally代码段中自动清除了ThreadLocal存储的对象。 如果代码在进入Executor前发生异常,就会导致线程不可用,这属于人为的Bug(例如接口方法和XML中的不匹配,导致找不到MappedStatement时),这种情况由于线程不可用,也不会导致ThreadLocal参数被错误的使用。

### 若依 Cloud 集成 MyBatis Plus 示例教程 若依 Cloud 是基于 Spring Cloud 的微服务框架,集成了多种主流技术栈,如 Nacos、Seata 等。在若依 Cloud 项目中集成 MyBatis Plus 可以进一步提升开发效率和代码质量。以下是具体的集成与使用方法: #### 1. 添加依赖 在项目的 `pom.xml` 文件中添加 MyBatis Plus 的相关依赖。确保引入的版本与项目中的 Spring Boot 和 Spring Cloud 版本兼容[^4]。 ```xml <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.3</version> </dependency> ``` #### 2. 配置数据源 在 `application.yml` 或 `application.properties` 文件中配置数据库连接信息。例如: ```yaml spring: datasource: url: jdbc:mysql://localhost:3306/your_database?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC username: root password: root driver-class-name: com.mysql.cj.jdbc.Driver ``` #### 3. 配置 MyBatis Plus 在 Spring Boot 的主类或配置类上添加注解 `@MapperScan`,用于扫描 Mapper 接口所在的包路径。例如: ```java import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import com.baomidou.mybatisplus.annotation.DbType; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; import org.mybatis.spring.annotation.MapperScan; @SpringBootApplication @MapperScan("com.example.mapper") // 替换为实际的 Mapper 包路径 public class RuoYiCloudApplication { public static void main(String[] args) { SpringApplication.run(RuoYiCloudApplication.class, args); } } ``` #### 4. 配置分页插件 MyBatis Plus 提供了强大的分页功能,可以通过配置分页插件来实现分页查询。在配置类中添加以下代码: ```java import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class MyBatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } } ``` #### 5. 创建实体类 根据数据库表结构创建对应的实体类。MyBatis Plus 支持自动映射字段名与实体类属性名。例如: ```java import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; @TableName("user") public class User { @TableId(type = IdType.AUTO) private Long id; private String name; private Integer age; // Getters and Setters } ``` #### 6. 创建 Mapper 接口 创建一个继承自 `BaseMapper` 的 Mapper 接口,用于操作数据库表。例如: ```java import com.baomidou.mybatisplus.core.mapper.BaseMapper; import org.apache.ibatis.annotations.Mapper; @Mapper public interface UserMapper extends BaseMapper<User> { } ``` #### 7. 使用 MyBatis Plus 通过注入 Mapper 接口,可以直接调用 MyBatis Plus 提供的 CRUD 方法。例如: ```java import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import java.util.List; @RestController public class UserController { @Autowired private UserMapper userMapper; @GetMapping("/users") public List<User> getUsers() { return userMapper.selectList(null); // 查询所有用户 } } ``` #### 8. 测试分页功能 可以使用 MyBatis Plus 提供的分页插件进行分页查询。例如: ```java import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; @GetMapping("/users/page") public IPage<User> getUsersByPage(int page, int size) { Page<User> pageInfo = new Page<>(page, size); return userMapper.selectPage(pageInfo, null); } ``` --- ### 注意事项 - 在实际开发中,建议结合分布式事务管理工具(如 Seata)处理跨服务的事务问题[^5]。 - 如果项目中使用了 Nacos 作为配置中心,可以将数据库连接信息等敏感数据存储在 Nacos 中,并通过动态刷新机制加载配置[^3]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值