温故而知新,这里记录一下
一、引言
分页查询每个人程序猿几乎都使用过,但是有部分同学不懂什么是物理分页和逻辑分页。
物理分页:
相当于执行了limit分页语句,返回部分数据。物理分页只返回部分数据占用内存小,能够获取数据库最新的状态,实施性比较强,一般适用于数据量比较大,数据更新比较频繁的场景。
逻辑分页:
一次性把全部的数据取出来,通过程序进行筛选数据。如果数据量大的情况下会消耗大量的内存,由于逻辑分页只需要读取数据库一次,不能获取数据库最新状态,实施性比较差,适用于数据量小,数据稳定的场合。
二、分页处理方式
2.1、MybatisPageHelper组件
常用的分页组件有MybatisPageHelper等,分页原理为先执行原SQL+limit语句,再执行select count(*) from xxx where xxx语句。
一共执行了两次,进行了两次数据的筛选和过滤。
2.1.1、MybatisPlusConfig配置
创建MybatisPlusConfig配置类,需要配置分页插件,小编使用的Spring boot配置方式。
/*** @Auther: IT贱男* @Date: 2019/6/12 15:06* @Description: MybatisPlus配置类*/
@Configuration
public class MyBatisPlusConfig {/*** 分页插件* @return*/@Beanpublic PaginationInterceptor paginationInterceptor() {return new PaginationInterceptor();}
}
2.1.2、具体分页实现
MP的Wrapper提供了两种分页查询的方式,源码如下:
/*** 根据 entity 条件,查询全部记录(并翻页)** @param page 分页查询条件(可以为 RowBounds.DEFAULT)* @param queryWrapper 实体对象封装操作类(可以为 null)*/IPage<T> selectPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);/*** 根据 Wrapper 条件,查询全部记录(并翻页)** @param page 分页查询条件* @param queryWrapper 实体对象封装操作类*/IPage<Map<String, Object>> selectMapsPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
可见两个分页方法参数都是一致的,只是返回参数略有不同,具体选择根据实际业务为准。
/*** 分页查询*/@Testpublic void selectByPage() {QueryWrapper<User> wrapper = new QueryWrapper();wrapper.like("name", "雨").lt("age", 40);Page<User> page = new Page<>(1,2);//IPage<User> userIPage = userMapper.selectPage(page, wrapper);IPage<Map<String, Object>> mapIPage = userMapper.selectMapsPage(page, wrapper);System.out.println("总页数"+mapIPage.getPages());System.out.println("总记录数"+mapIPage.getTotal());List<Map<String, Object>> records = mapIPage.getRecords();records.forEach(System.out::println);}
以上分页查询执行sql如下,先是查询了一次总记录数,然后在查询的数据。
DEBUG==> Preparing: SELECT COUNT(1) FROM user WHERE name LIKE ? AND age < ?
DEBUG==> Parameters: %雨%(String), 40(Integer)
TRACE<== Columns: COUNT(1)
TRACE<== Row: 2
DEBUG==> Preparing: SELECT id,name,age,email,manager_id,create_time FROM user WHERE name LIKE ? AND age < ? LIMIT ?,?
DEBUG==> Parameters: %雨%(String), 40(Integer), 0(Long), 2(Long)
TRACE<== Columns: id, name, age, email, manager_id, create_time
TRACE<== Row: 2, 张雨琪, 31, zjq@baomidou.com, 1088248166370832385, 2019-01-14 09:15:15
TRACE<== Row: 3, 刘红雨, 31, lhm@baomidou.com, 1088248166370832385, 2019-01-14 09:48:16
DEBUG<== Total: 2
总页数1
总记录数2
可以看出,质量量两条查询语句:
- SELECT COUNT(1) FROM user WHERE name LIKE ? AND age < ?
- SELECT id,name,age,email,manager_id,create_time FROM user WHERE name LIKE ? AND age < ? LIMIT ?,?
2.2 mysql SQL_CALC_FOUND_ROWS & FOUND_ROWS
可以使用MySQL “SQL_CALC_FOUND_ROWS & FOUND_ROWS()” ,只需进行一次数据的筛选和过滤。(FOUND_ROWS()函数用于统计limit之前查找到的行数)。
2.2.1 例子
实际项目中使用
(1)UserMapper.xml
<resultMap type="com.sys.entity.User" id="UserResult"><id property="userId" column="user_id" /><result property="deptId" column="dept_id" /><result property="userName" column="user_name" /><result property="nickName" column="nick_name" /><result property="roleIds" column="role_ids" /><result property="password" column="password" /><result property="createBy" column="create_by" /><result property="createTime" column="create_time" /><result property="updateBy" column="update_by" /><result property="updateTime" column="update_time" /><result property="remark" column="remark" />
</resultMap><resultMap type="java.lang.Integer" id="count"><result column="total"/>
</resultMap><select id="selectList" resultMap="UserResult,count">SELECT SQL_CALC_FOUND_ROWS a.user_id,a.user_name,a.nick_name,dept_name ,c.role_nameFROM sys_user aLEFT JOIN sys_dept b on a.dept_id=b.dept_idLEFT JOIN sys_role c on a.role_ids=c.role_id<where><if test="userName != null and userName != ''">AND u.user_name like concat('%', #{userName}, '%')</if><if test="deptName != null and deptName != ''">AND d.dept_name like concat('%', #{deptName}, '%')</if></where>order by create_time descLIMIT ${pageSize * (pageNum-1)},${pageSize};SELECT FOUND_ROWS() AS total;
</select>
(2)UserMapper.java
List<Object> selectList(@Param("pageSize") Integer pageSize,@Param("pageNum") Integer pageNum,@Param("userName") String userName,@Param("deptName") String deptName);
(3)UserService.java
public List<Object> selectList(Integer pageSize,Integer pageNum,String userName,String deptName) {return userMapper.selectList(pageSize,pageNum,userName,deptName);
}
(4)UserController.java
/*** 获取用户列表* @return 用户列表*/@GetMapping("/list")public ResultVo list(Integer pageSize, Integer pageNum,String userName,String deptName){List<Object> objList = userService.selectList(pageSize, pageNum, userName,deptName);List<User> list = (List<User>) objList.get(0);int count = ((List<Integer>) objList.get(1)).get(0);//总数Page<User> page = new Page<>(pageSize, pageNum);page.setSize(pageSize);page.setCurrent(pageNum);page.setTotal(count);//计算分页总页数page.setPages(count %10 == 0 ? count/10 :count/10+1);page.setRecords(list);return ResultVoUtil.success(page);}
最后,还需要在yml或propertis配置文件中,配置数据库连接池那里,在 url 中,加上 allowMultiQueries=true
2.3、mysql-mybatis-pagination组件
mysql-mybatis-pagination是一个基于MySQL方言 “SQL_CALC_FOUND_ROWS & FOUND_ROWS()” 的轻量级分页组件。
<dependency><groupId>io.github.flashvayne</groupId><artifactId>mysql-mybatis-pagination</artifactId><version>1.0.1</version> </dependency>
项目github地址:mysql-mybatis-pagination
MySQL分页组件mysql-mybatis-pagination|Springboot|Mybatis | Vayne的博客
三、参考
MyBatis或MyBatis-plus中分页查询同时查询数据和总数量_mybatisplus分页查询到的总数据量-CSDN博客
MySQL分页组件mysql-mybatis-pagination|Springboot|Mybatis | Vayne的博客
Mybatis分页查询和总条数避免查询两次的方法 - 简书
https://www.cnblogs.com/FlyGoldfish/articles/16615357.html