Mybatis Plus 分页查询数据(图解)
一则或许对你有用的小广告
欢迎加入小哈的星球 ,你将获得:专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论
- 新项目:《从零手撸:仿小红书(微服务架构)》 正在持续爆肝中,基于
Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...
,点击查看项目介绍 - 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/
截止目前, 星球 内专栏累计输出 72w+ 字,讲解图 3103+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 2400+ 小伙伴加入学习 ,欢迎点击围观
大家好,我是小哈。
本小节中,我们将学习如何通过 Mybatis Plus 分页查询数据库表中的数据。
什么是分页查询?
下图是小哈从京东上查询关键词 「手机」 ,展示的手机数据就是分页查询,共有 91 页:
分页查询就是把需要查询的数据集进行分批展示,比如商品表中有 1万 条手机数据,每页按固定数量展示。
为什么需要分页查询?
-
前端页面能够展示的内容有限;
-
当数据库中数据量太多,比如 100W 条,一次性全部返回,查询速度慢,而且内存也顶不住;
表结构
了解到分页查询相关概念后,我们来上手 Mybatis Plus 的分页查询功能,还是之前小节中定义好的用户测试表, 执行脚本如下:
DROP TABLE IF EXISTS user;
CREATE TABLE `user` (
`id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`name` varchar(30) NOT NULL DEFAULT '' COMMENT '姓名',
`age` int(11) NULL DEFAULT NULL COMMENT '年龄',
`gender` tinyint(2) NOT NULL DEFAULT 0 COMMENT '性别,0:女 1:男',
PRIMARY KEY (`id`)
) COMMENT = '用户表';
定义实体类
定义一个名为 User
实体类:
@TableName("user")
public class User {
/**
* 主键 ID, @TableId 注解定义字段为表的主键,type 表示主键类型,IdType.AUTO 表示随着数据库 ID 自增
*/
@TableId(type = IdType.AUTO)
private Long id;
/**
* 姓名
*/
private String name;
/**
* 年龄
*/
private Integer age;
/**
* 性别
*/
private Integer gender;
}
不明白 Mybatis Plus 实体类注解的小伙伴,可参考前面小节 , 有详细解释。
新增测试数据
分页查询前,先通过代码插入一些测试数据,执行代码如下:
// 循环插入 100 条测试数据
for (int i = 0; i < 100; i++) {
User user = new User();
user.setName("犬小哈" + i);
user.setAge(i);
user.setGender(1);
userMapper.insert(user);
}
TIP : 不清楚如何插入数据可翻阅前面 《新增数据》 小节;
添加分页插件
接着,在 MybatisPlusConfig
配置类中,添加分页插件 PaginationInnerInterceptor
:
/**
* @Author: 犬小哈
* @From: 公众号:小哈学Java, 网站:www.quanxiaoha.com
* @Date: 2022-12-15 18:29
* @Version: v1.0.0
* @Description: TODO
**/
@Configuration
@MapperScan("com.quanxiaoha.mybatisplusdemo.mapper")
public class MybatisPlusConfig {
/**
* 分页插件
* @return
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
return interceptor;
}
}
开始分页查询数据
Mybatis Plus 对 Mapper 层和 Service 层都将常见的增删改查操作封装好了,只需简单的继承,即可轻松搞定对数据的增删改查,本文重点讲解分页查询相关的部分。
Mapper 层
定义一个 UserMapper
, 让其继承自 BaseMapper
:
public interface UserMapper extends BaseMapper<User> {
}
然后,注入 Mapper :
@Autowired
private UserMapper userMapper;
BaseMapper
提供的分页查询相关的方法如下:
解释一下每个方法的作用:
// 分页查询,page 用于设置需要查询的页数,以及每页展示数据量,wrapper 用于组装查询条件
IPage<T> selectPage(IPage<T> page, Wrapper<T> queryWrapper);
// 同上,区别是用 map 来接受查询的数据
IPage<Map<String, Object>> selectMapsPage(IPage<T> page, Wrapper<T> queryWrapper);
参数说明:
类型 | 参数名 | 描述 |
---|---|---|
Wrapper<T> | queryWrapper | 实体对象封装操作类(可以为 null ) |
IPage<T> | page | 分页查询条件(可以为 RowBounds.DEFAULT ) |
示例代码
接下来,小哈来演示一些示例代码以便你快速了解如何使用分页查询:
// 组装查询条件
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
// where age = 30
queryWrapper.eq("age", 30);
// 查询第 2 页数据,每页 10 条
Page<User> page = new Page<>(2, 10);
page = userMapper.selectPage(page, queryWrapper);
System.out.println("总记录数:" + page.getTotal());
System.out.println("总共多少页:" + page.getPages());
System.out.println("当前页码:" + page.getCurrent());
// 当前页数据
List<User> users = page.getRecords();
执行上面的代码,实际上执行了两条 SQL : 先执行 COUNT(*)
查询出记录总数,然后才是分页语句 LIMIT
:
Page 类说明
该类继承了
IPage
类,实现了 简单分页模型 ,如果你要实现自己的分页模型可以继承Page
类或者实现IPage
类
属性名 | 类型 | 默认值 | 描述 |
---|---|---|---|
records | List | emptyList | 查询数据列表 |
total | Long | 0 | 查询列表总记录数 |
size | Long | 10 | 每页显示条数,默认 10 |
current | Long | 1 | 当前页 |
orders | List | emptyList | 排序字段信息,允许前端传入的时候,注意 SQL 注入问题,可以使用 SqlInjectionUtils.check(...) 检查文本 |
optimizeCountSql | boolean | true | 自动优化 COUNT SQL 如果遇到 jSqlParser 无法解析情况,设置该参数为 false |
optimizeJoinOfCountSql | boolean | true | 自动优化 COUNT SQL 是否把 join 查询部分移除 |
searchCount | boolean | true | 是否进行 count 查询,如果指向查询到列表不要查询总记录数,设置该参数为 false |
maxLimit | Long | 单页分页条数限制 | |
countId | String | xml 自定义 count 查询的 statementId |
Service 层
Mybatis Plus 同样也封装了通用的 Service 层 CRUD 操作,并且提供了更丰富的方法。接下来,我们上手看 Service 层的代码结构,如下图:
先定义 UserService
接口 ,让其继承自 IService
:
public interface UserService extends IService<User> {
}
再定义实现类 UserServiceImpl
,让其继承自 ServiceImpl
, 同时实现 UserService
接口,这样就可以让 UserService
拥有了基础通用的 CRUD 功能,当然,实际开发中,业务会更加复杂,就需要向 IService
接口自定义方法并实现:
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
}
注入 UserService
:
@Autowired
private UserService userService;
Service 层封装的分页相关方法如下:
// 无条件分页查询
IPage<T> page(IPage<T> page);
// 条件分页查询
IPage<T> page(IPage<T> page, Wrapper<T> queryWrapper);
// 无条件分页查询
IPage<Map<String, Object>> pageMaps(IPage<T> page);
// 条件分页查询
IPage<Map<String, Object>> pageMaps(IPage<T> page, Wrapper<T> queryWrapper);
示例代码
Service 层的分页方法入参和 Mapper 差不多:
// 组装查询条件
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
// where age = 30
queryWrapper.eq("age", 30);
// 查询第 2 页数据,每页 10 条
Page<User> page = new Page<>(2, 10);
page = userService.page(page, queryWrapper);
System.out.println("总记录数:" + page.getTotal());
System.out.println("总共多少页:" + page.getPages());
System.out.println("当前页码:" + page.getCurrent());
// 当前页数据
List<User> users = page.getRecords();