之前在写博客系统前台页面的时候,遇到了利用mp进行分页查询的情况,由于涉及到的知识点相对较为重要,固写一篇博客以此巩固。
一、功能需求
在首页和分类页面都需要查询文章列表。
- 首页:查询所有的文章
- 分类页面:查询对应分类下的文章
- 要求:①只能查询正式发布的文章 ②置顶的文章要显示在最前面
1.1 接口设计
Swagger2如下:
请求参数如下:
响应如下:
二、代码实现
由于我们涉及了利用mp进行分页的情况,所以我们需要先配置mp的配置类:
@Configuration
public class MbatisPlusConfig {/*** 3.4.0之后版本* @return*/@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor(){MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());return mybatisPlusInterceptor;}
}
在ArticleController中:
@RestController
@RequestMapping("/article")
public class ArticleController {@Autowiredprivate 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 {@Autowiredprivate CategoryService categoryService;@Overridepublic 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();//查询categoryNamearticles.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 {@TableIdprivate Long id;//标题private String title;//文章内容private String content;//文章摘要private String summary;//所属分类idprivate 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();//实现属性copyBeanUtils.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,那么就会报错。