基于SpringBoot、MybatisPlus以及knife4j的MP分页查询demo

1.设计数据库表

在实现基于SpringBoot、MybatisPlus、knife4j的MP分页查询之前需要先设计数据库以及表,由于本次demo不是严格意义上的项目,因此设计表时没有太下功夫.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
/*
Navicat Premium Data Transfer

Source Server : MySQL80
Source Server Type : MySQL
Source Server Version : 80030
Source Host : localhost:3306
Source Schema : mp_test

Target Server Type : MySQL
Target Server Version : 80030
File Encoding : 65001

Date: 11/12/2023 17:09:05
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for user
-- ----------------------------
CREATE DATABASE MP_TEST;
USE MP_TEST;
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`user_id` int NOT NULL AUTO_INCREMENT COMMENT '用户id,自增',
`username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '用户账号',
`password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '用户密码',
`sex` tinyint NOT NULL COMMENT '用户性别 0:女 1:男',
`phone` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '用户手机号',
`create_time` datetime NOT NULL COMMENT '用户账号创建时间',
`update_time` datetime NOT NULL COMMENT '用户账号更新时间',
PRIMARY KEY (`user_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES (1, '张飞', '12345678', 1, '15699999999', '2023-12-11 10:48:28', '2023-12-11 10:48:33');
INSERT INTO `user` VALUES (2, '关羽', '12345678', 1, '15699999999', '2023-12-11 10:48:28', '2023-12-11 10:48:33');
INSERT INTO `user` VALUES (3, '刘备', '12345678', 1, '15699999999', '2023-12-11 10:48:28', '2023-12-11 10:48:33');
INSERT INTO `user` VALUES (4, '曹操', '12345678', 1, '15699999999', '2023-12-11 10:48:28', '2023-12-11 10:48:33');
INSERT INTO `user` VALUES (5, '孙权', '12345678', 1, '15699999999', '2023-12-11 10:48:28', '2023-12-11 10:48:33');
INSERT INTO `user` VALUES (6, '孙尚香', '12345678', 0, '15699999999', '2023-12-11 10:48:28', '2023-12-11 10:48:33');
INSERT INTO `user` VALUES (7, '曹爽', '12345678', 1, '15699999999', '2023-12-11 10:48:28', '2023-12-11 10:48:33');
INSERT INTO `user` VALUES (8, '徐盛', '12345678', 1, '15699999999', '2023-12-11 10:48:28', '2023-12-11 10:48:33');
INSERT INTO `user` VALUES (9, '诸葛亮', '12345678', 1, '15699999999', '2023-12-11 10:48:28', '2023-12-11 10:48:33');
INSERT INTO `user` VALUES (10, '鲍三娘', '12345678', 0, '15699999999', '2023-12-11 10:48:28', '2023-12-11 10:48:33');
INSERT INTO `user` VALUES (11, '王五', '12345678', 1, '15699999999', '2023-12-11 10:48:28', '2023-12-11 10:48:33');
INSERT INTO `user` VALUES (12, '李四', '12345678', 1, '15699999999', '2023-12-11 10:48:28', '2023-12-11 10:48:33');
INSERT INTO `user` VALUES (13, '张三', '12345678', 1, '15699999999', '2023-12-11 10:48:28', '2023-12-11 10:48:33');
INSERT INTO `user` VALUES (14, '虞姬', '12345678', 0, '15699999999', '2023-12-11 10:48:28', '2023-12-11 10:48:33');
INSERT INTO `user` VALUES (15, '项羽', '12345678', 1, '15699999999', '2023-12-11 10:48:28', '2023-12-11 10:48:33');
INSERT INTO `user` VALUES (16, '刘邦', '12345678', 1, '15699999999', '2023-12-11 10:48:28', '2023-12-11 10:48:33');
INSERT INTO `user` VALUES (17, '张文', '12345678', 1, '15699999999', '2023-12-11 10:48:28', '2023-12-11 10:48:33');
INSERT INTO `user` VALUES (18, '张武', '12345678', 0, '15699999999', '2023-12-11 10:48:28', '2023-12-11 10:48:33');
INSERT INTO `user` VALUES (19, '张二', '12345678', 1, '15699999999', '2023-12-11 10:48:28', '2023-12-11 10:48:33');
INSERT INTO `user` VALUES (20, '张一', '12345678', 0, '15699999999', '2023-12-11 10:48:28', '2023-12-11 10:48:33');

SET FOREIGN_KEY_CHECKS = 1;

2.创建SpringBoot项目并进行配置

接下来创建SpringBoot项目.

SpringBoot项目创建完成后引入本次demo所需依赖以及配置项目.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--mysql-->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.0.33</version>
</dependency>
<!--数据库连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.3</version>
</dependency>
<!--lombok注解类-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!--单元测试类-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--mybatis-plus依赖-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test</artifactId>
</dependency>
<!--knife4j-->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>
<!--hutool工具 用于拷贝对象和集合-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.11</version>
</dependency>
</dependencies>

导入demo所需依赖后还需要进行配置,在application.yml中进行配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#端口号
server:
port: 8888
spring:
datasource:
# 数据库驱动
driver-class-name: com.mysql.cj.jdbc.Driver
# 数据库url
url: jdbc:mysql://localhost:3306/mp_test?useSSL=false&serverTimezone=UTC
# 数据库用户名
username: root
# 数据库密码
password:
# 这里需要开启这个配置,不然后面的knife4j启动时会出现依赖冲突的问题
mvc:
pathmatch:
matching-strategy: ant_path_matcher
# 扫描mapper.xml文件
mybatis-plus:
mapper-locations: classpath*:mapper/*.xml
configuration:
# 开启驼峰命名
map-underscore-to-camel-case: true

配置项准备好后,主备两个配置类,分别是MybatisPlus分页插件类以及knif4j的配置类.

MybatisPlus插件配置类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
// 获取分页拦截器
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
// 获取分页插件
PaginationInnerInterceptor innerInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
// 设置分页查询上限
innerInterceptor.setMaxLimit(1000L);
// 将插件添加到拦截器
mybatisPlusInterceptor.addInnerInterceptor(innerInterceptor);
return mybatisPlusInterceptor;
}
}

Knife4j配置类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
@Slf4j
@Configuration
@EnableSwagger2WebMvc
// 对JSR303提供支持
@Import(BeanValidatorPluginsConfiguration.class)
public class Knife4jConfig {
@Bean
public Docket defaultApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.groupName("基于MybatisPlus的分页查询")
.select()
// 添加@Api注解才显示
.apis(RequestHandlerSelectors.withClassAnnotation(Api.class))
// 这里指定Controller扫描包路径
.apis(RequestHandlerSelectors.basePackage("xju.edu"))
.paths(PathSelectors.any())
.build();
}

/**
* swagger-api接口描述信息
*/
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("基于MybatisPlus的分页查询")
.description("MybatisPlus分页查询demo")
.contact(
new Contact(
"yang",
"http://localhost:8888",
"2098116562@qq.com"
)
)
.version("1.0.0")
.build();
}
}

3.代码生成

使用MP的代码生成功能根据数据库的表生成代码.

进行简单的信息填写后,点击code generatro,自动生成代码

4.编写DTO,Query,VO

PageDTO用于统一结果返回

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Data
public class PageDTO<T> {
/**
* 返回的总页数
*/
private Long totalPage;
/**
* 返回的总条数
*/
private Long totalRecords;
/**
* 返回数据列表
* T 泛型
*/
private List<T> list;
}

UserVO用于返回前端展示视图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserVO {
/**
* 用户id
*/
private Integer userId;
/**
* 用户名
*/
private String username;
/**
* 用户密码
*/
private String password;
/**
* 用户性别 0 女 1 男
*/
private Integer sex;
/**
* 用户手机号
*/
private String phone;
}

Query用于接收前端传递的参数然后进行后端查询

UserQuery

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@EqualsAndHashCode(callSuper = true)
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserQuery extends PageQuery{
/**
* 用户id
*/
private Integer userId;
/**
* 用户名
*/
private String username;
/**
* 用户密码
*/
private String password;
/**
* 用户性别 0 女 1 男
*/
private Integer sex;
/**
* 用户手机号
*/
private String phone;
}

PageQuery:对所有需求都需要进行查询的内容进行封装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Data
@AllArgsConstructor
@NoArgsConstructor
public class PageQuery {
/**
* 页码
*/
private Integer pageNo = 1;
/**
* 每页显示
*/
private Integer pageSize = 10;
/**
* 按照什么字段排序
*/
private String sortBy;
/**
* 升序降序
*/
private Boolean isAsc = false;

5.编写业务层

5.1 根据pageNo,pageSize,sortBy,isAsc进行分页查询

这个需求是前端不传递任何关键字进行分页查询,即用户想要获取第pageNo页的pageSize条数据

实现的思路是首先使用Page.of(pageNo,pageSize)进行分页设置,然后判断前端是否传了排序关键字,如果没有则按照user_id进行升序排序,否则按照用户传递的进行排序.经过设置后获取到Page 的对象.使用lambdaQuery.page()方法将之前获取到的Page对象传入进行MP分页查询.然后进行返回结果封装.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/**
* 用户查询全部分页查询
* @param pageQuery
* @return
*/
@Override
public PageDTO<UserVO> userAllPageQuery(PageQuery pageQuery) {

Page<User> userPage = Page.of(pageQuery.getPageNo(), pageQuery.getPageSize());
// 判断前端是否传递了排序字段以及升序降序
if (StringUtils.isBlank(pageQuery.getSortBy())){
// 前端没有传递排序的条件 默认按照id进行升序排序
userPage.addOrder(new OrderItem("user_id",true));
} else {
// 前端传递了条件
userPage.addOrder(new OrderItem(pageQuery.getSortBy(),pageQuery.getIsAsc()));
}
// 使用lambdaQuery进行分页查询
Page<User> page = lambdaQuery().page(userPage);
PageDTO<UserVO> pageDTO = new PageDTO<>();
pageDTO.setTotalPage(page.getPages());
pageDTO.setTotalRecords(page.getTotal());
// 获取数据
List<User> users = page.getRecords();
// 将PO -> VO
List<UserVO> userVOs = BeanUtil.copyToList(users, UserVO.class);
// 将VO封装到pageDTO中
pageDTO.setList(userVOs);
return pageDTO;
}

5.2 根据前端传入关键字进行分页查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
/**
* 根据前端的条件分页查询
* @param userQuery 前端传来的分页查询条件
* @return
*/
@Override
public PageDTO<UserVO> userPageQuery(UserQuery userQuery) {
if (userQuery == null){
throw new RuntimeException("用户为空");
}
// 构建Page分页条件
Page<User> page = Page.of(userQuery.getPageNo(), userQuery.getPageSize());

// 判断前端传来的字段排序是否为空
if (StringUtils.isBlank(userQuery.getSortBy())){
// 如果前端传来的数据为空 则默认设置排序字段为userId 升序排列
page.addOrder(new OrderItem("userId",true));
} else {
// 前端传来的排序字段不为空,则设置排序字段为前端传来的字段
page.addOrder(new OrderItem(userQuery.getSortBy(),userQuery.getIsAsc()));
}
// 使用LambdaQuery进行分页查询
Page<User> userPage = lambdaQuery().like(userQuery.getUsername() != null, User::getUsername, userQuery.getUsername())
.eq(userQuery.getPassword() != null, User::getPassword, userQuery.getPassword())
.eq(userQuery.getSex() != null, User::getSex, userQuery.getSex())
.eq(userQuery.getPhone() != null, User::getPhone, userQuery.getPhone()).page(page);
// 我们要返回的是PageDTO<UserVo>
PageDTO<UserVO> pageDTO = new PageDTO<>();
pageDTO.setTotalPage(userPage.getPages());
pageDTO.setTotalRecords(userPage.getTotal());
// 获取分页数据
List<User> users = userPage.getRecords();
// 看是否查询到了用户
if (CollectionUtils.isEmpty(users)){
// 查询的用户为空
pageDTO.setList(Collections.emptyList());
return pageDTO;
}
// 查询到了用户 将PO -> VO
List<UserVO> userVO = BeanUtil.copyToList(users, UserVO.class);
pageDTO.setList(userVO);
return pageDTO;
}

思路同之前的差不多

6.进行业务代码改进

之前的代码实现逻辑还是比较复杂,现在根据之前的博客进行代码优化.

将设置分页条件的代码封装为方法:

封装前的方法

1
2
3
4
5
6
7
8
9
Page<User> userPage = Page.of(pageQuery.getPageNo(), pageQuery.getPageSize());
// 判断前端是否传递了排序字段以及升序降序
if (StringUtils.isBlank(pageQuery.getSortBy())){
// 前端没有传递排序的条件 默认按照id进行升序排序
userPage.addOrder(new OrderItem("user_id",true));
} else {
// 前端传递了条件
userPage.addOrder(new OrderItem(pageQuery.getSortBy(),pageQuery.getIsAsc()));
}

封装后的方法代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
@Data
@AllArgsConstructor
@NoArgsConstructor
public class PageQuery {
/**
* 页码
*/
private Integer pageNo = 1;
/**
* 每页显示
*/
private Integer pageSize = 10;
/**
* 按照什么字段排序
*/
private String sortBy;
/**
* 升序降序
*/
private Boolean isAsc = false;

/**
* 将前端传递的query对象转换为MP的Page对象设置排序字段并返回
* @param orderItems 排序条件
* @param <T>
* @return
*/
public <T> Page<T> toMpPage(OrderItem ... orderItems){
// 设置分页条件
Page<T> page = Page.of(pageNo, pageSize);
// 设置排序条件
// 先判断传递的orderItems是否为空
if (StringUtils.isBlank(getSortBy())){
// 传递的排序字段为空
page.addOrder(orderItems);
} else {
// 排序的字段不为空
page.addOrder(new OrderItem(getSortBy(),getIsAsc()));
}
return page;
}

/**
* 按照userId进行升序排序
* @param orderItems
* @param <T>
* @return
*/
public <T> Page<T> toMpPageByUserId(OrderItem ... orderItems){
return toMpPage(new OrderItem("user_id",true));
}
}

将分页条件查询方法封装为方法:

封装前的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 使用LambdaQuery进行分页查询
Page<User> userPage = lambdaQuery().like(userQuery.getUsername() != null, User::getUsername, userQuery.getUsername())
.eq(userQuery.getPassword() != null, User::getPassword, userQuery.getPassword())
.eq(userQuery.getSex() != null, User::getSex, userQuery.getSex())
.eq(userQuery.getPhone() != null, User::getPhone, userQuery.getPhone()).page(page);
// 我们要返回的是PageDTO<UserVo>
PageDTO<UserVO> pageDTO = new PageDTO<>();
pageDTO.setTotalPage(userPage.getPages());
pageDTO.setTotalRecords(userPage.getTotal());
// 获取分页数据
List<User> users = userPage.getRecords();
// 看是否查询到了用户
if (CollectionUtils.isEmpty(users)){
// 查询的用户为空
pageDTO.setList(Collections.emptyList());
return pageDTO;
}
// 查询到了用户 将PO -> VO
List<UserVO> userVO = BeanUtil.copyToList(users, UserVO.class);
pageDTO.setList(userVO);
return pageDTO;

将部分代码抽取为通用方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
@Data
public class PageDTO<T> {
/**
* 返回的总页数
*/
private Long totalPage;
/**
* 返回的总条数
*/
private Long totalRecords;
/**
* 返回数据列表
* T 泛型
*/
private List<T> list;


/**
* userPage转换为PageDTO
* @return
*/
public static <PO,VO> PageDTO<VO> of(Page<PO> userPage, Class<VO> voClass){
// 封装VO结果
PageDTO<VO> dto = new PageDTO<>();
dto.setTotalRecords(userPage.getTotal());
dto.setTotalPage(userPage.getPages());
// 获取数据
List<PO> users = userPage.getRecords();
// 将users -> userVo
if (CollUtil.isEmpty(users)){
// 如果没查到
dto.setList(Collections.emptyList());
return dto;
}
// 拷贝UserVo
List<VO> userVOs = BeanUtil.copyToList(users, voClass);
dto.setList(userVOs);
return dto;
}
}

改善后的业务层代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* 根据前端的条件分页查询
* @param userQuery 前端传来的分页查询条件
* @return
*/
@Override
public PageDTO<UserVO> userPageQuery(UserQuery userQuery) {
Page<User> page = userQuery.toMpPage();
// 使用LambdaQuery进行分页查询
Page<User> userPage = lambdaQuery().like(userQuery.getUsername() != null, User::getUsername, userQuery.getUsername())
.eq(userQuery.getPassword() != null, User::getPassword, userQuery.getPassword())
.eq(userQuery.getSex() != null, User::getSex, userQuery.getSex())
.eq(userQuery.getPhone() != null, User::getPhone, userQuery.getPhone()).page(page);
return PageDTO.of(userPage,UserVO.class);
}

7.前后端联合测试

启动SpringBoot项目,访问localhost:8888/doc.html,其中8888是自己配置的application.yml中的port端口号.

启动成功出现这个界面就是配置成功

7.1 测试根据pageNo,pageSize进行分页查询

传递参数为

1
2
3
4
{
"pageNo" : 1,
"pageSize" : 5
}

意为查询第一页,一页展示5条数据,返回结果

查询成功

7.2 根据前端传入关键字进行分页查询

根据条件查询

传递的数据为:

1
2
3
4
5
6
7
{
"isAsc": true,
"pageNo": 1,
"pageSize": 5,
"sortBy": "user_id",
"username": "张"
}

查找姓名中带张的所有用户,查询第一页,一页5条数据并以user_id查询进行升序排序

查询成功

8.总结

实现了基于SpringBoot,MybatisPlus,Knife4j的MP分页查询demo.

  • Copyright: Copyright is owned by the author. For commercial reprints, please contact the author for authorization. For non-commercial reprints, please indicate the source.
  • Copyrights ©本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处! yang
  • Visitors: | Views:

请我喝杯咖啡吧~

支付宝
微信