之前在写博客系统前台页面的时候,遇到了利用mp进行分页查询的情况,由于涉及到的知识点相对较为重要,固写一篇博客以此巩固。
一、功能需求
在首页和分类页面都需要查询文章列表。
- 首页:查询所有的文章
- 分类页面:查询对应分类下的文章
- 要求:①只能查询正式发布的文章 ②置顶的文章要显示在最前面
1.1 接口设计
Swagger2如下:
请求参数如下:
响应如下:
二、代码实现
由于我们涉及了利用mp进行分页的情况,所以我们需要先配置mp的配置类:
@Configuration
public class MbatisPlusConfig {
/**
* 3.4.0之后版本
* @return
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
return mybatisPlusInterceptor;
}
}
在ArticleController中:
@RestController
@RequestMapping("/article")
public class ArticleController {
@Autowired
private ArticleService articleService;
@GetMapping("/articleList")
public ResponseResult articleList(Integer pageNum,Integer pageSize,Long categoryId){
return articleService.articleList(pageNum,pageSize,categoryId);
}
}
分析:
这里前端传来的请求参数类型是 query 类型,如果我们需要解决前端和后端中命名不一致的问题,可以使用@RequestParms注解。关于SpringMVC的相关知识可以移步博客的另一篇博客:
SpringMVC的三大功能
ArticleService中:
在ArticleServiceImpl中:
@Service
public class ArticleServiceImpl extends ServiceImpl<ArticleMapper, Article> implements ArticleService {
@Autowired
private CategoryService categoryService;
@Override
public ResponseResult articleList(Integer pageNum, Integer pageSize, Long categoryId) {
//查询条件
LambdaQueryWrapper<Article> lambdaQueryWrapper = new LambdaQueryWrapper<>();
// 如果 有categoryId 就要 查询时要和传入的相同
lambdaQueryWrapper.eq(Objects.nonNull(categoryId)&&categoryId>0 ,Article::getCategoryId,categoryId);
// 状态是正式发布的
lambdaQueryWrapper.eq(Article::getStatus,SystemConstants.ARTICLE_STATUS_NORMAL);
// 对isTop进行降序
lambdaQueryWrapper.orderByDesc(Article::getIsTop);
//分页查询
Page<Article> page = new Page<>(pageNum,pageSize);
page(page,lambdaQueryWrapper);
List<Article> articles = page.getRecords();
//查询categoryName
articles.stream()
.map(article -> article.setCategoryName(categoryService.getById(article.getCategoryId()).getName()))
.collect(Collectors.toList());
//articleId去查询articleName进行设置
// for (Article article : articles) {
// Category category = categoryService.getById(article.getCategoryId());
// article.setCategoryName(category.getName());
// }
//封装查询结果
List<ArticleListVo> articleListVos = BeanCopyUtils.copyBeanList(page.getRecords(), ArticleListVo.class);
PageVo pageVo = new PageVo(articleListVos,page.getTotal());
return ResponseResult.okResult(pageVo);
}
}
分析:
这里涉及到蛮多知识点的,如VO、Bean工具类拷贝等。
我们这里重点关注data里面的数据,外部的code、data、msg为我自定义的响应类 ReponseResult。
这里其实就涉及到VO的概念,这个data里面其实可以看出来,应该是由两个部分组成,一个是rows,一个是total。
因此,我们需要创建一个PageVo的概念:
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class PageVo {
private List rows;
private Long total;
}
而这个rows我们使用List集合接收,但是我们仔细观察Article类,发现它实际的字段是多于我们需要传输给前端的字段的,并且有些字段Article类是没有的,这里为了不破坏Article的耦合性,所以我们又需要创建一个ArticleListVo对象:
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ArticleListVo {
private Long id;
//标题
private String title;
//文章摘要
private String summary;
//所属分类名
private String categoryName;
//缩略图
private String thumbnail;
//访问量
private Long viewCount;
private Date createTime;
}
Article对象如下:
import java.util.Date;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
/**
* 文章表(Article)表实体类
*
* @author makejava
* @since 2022-02-01 11:36:28
*/
@SuppressWarnings("serial")
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("sg_article")
@Accessors(chain = true)
public class Article {
@TableId
private Long id;
//标题
private String title;
//文章内容
private String content;
//文章摘要
private String summary;
//所属分类id
private Long categoryId;
@TableField(exist = false)
private String categoryName;
//缩略图
private String thumbnail;
//是否置顶(0否,1是)
private String isTop;
//状态(0已发布,1草稿)
private String status;
//访问量
private Long viewCount;
//是否允许评论 1是,0否
private String isComment;
@TableField(fill = FieldFill.INSERT)
private Long createBy;
@TableField(fill = FieldFill.INSERT)
private Date createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Long updateBy;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
//删除标志(0代表未删除,1代表已删除)
private Integer delFlag;
public Article(Long id, long viewCount) {
this.id = id;
this.viewCount = viewCount;
}
}
细心观察可以发现,Article类虽然有大部分的ArticleListVo对象的属性,但是却缺少一个categoryName属性:
这里是因为Article表是没有这个属性的,所以我们加入这个@TableFiled注解,来避免mp在进行ORM映射的时候找不到属性而报错。
给Article类添加这个属性只是为了方便后面进行Bean拷贝而已,因为Article类必须有这个属性,才能将这个属性拷贝到我们要传输给前端的ArticleListVo属性。
可以通过for循环或者是Stream流的方式来将Article这个类的CategoryName属性填充完毕:
最后再进行Bean拷贝:
package com.fox.utils;
import org.springframework.beans.BeanUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public class BeanCopyUtils {
//由于这是工具类,就直接将构造方法设置为私有的
private BeanCopyUtils() {
}
public static <T> T copyBean(Object source,Class<T> clazz) {
//创建目标对象
T result = null;
try {
result = clazz.newInstance();
//实现属性copy
BeanUtils.copyProperties(source, result);
} catch (Exception e) {
e.printStackTrace();
}
//返回结果
return result;
}
public static <E,T> List<T> copyBeanList(List<E> list, Class<T> clazz){
List<T> result = new ArrayList<>();
for (E e : list) {
T t = copyBean(e, clazz);
result.add(t);
}
return result;
// return list.stream()
// .map(E -> copyBean(E, clazz))
// .collect(Collectors.toList());
}
}
需要注意的是,这里在进行Bean拷贝的时候,其会根据属性名自动匹配,但是需要保证拷贝的双方属性的类型是一致的,不然会转换失败,比如 Article 类中 id属性的类型是 long,而其Vo对象的id属性为 Integer,那么就会报错。