Spring Boot 博客系统项目开发之分页功能及侧边栏功能
感兴趣的朋友可以去关注一下。
文章总览
首页的前端页面编码完成,但是页面上都是静态数据,并没有与后端交互进行实际数据的读取和渲染,本节实验将会讲解首屏页面上数据的查询,以及如何将这些数据填充到页面中,前面一个实验更多的偏重于页面制作,本文则是数据填充和功能实现,数据主要有左侧功能栏中的博客统计列表数据、标签栏数据、首页文章列表数据以及分页功能实现。
知识点
最新发布
点击最多
文章列表
分页功能
环境
JDK 1.8 或者更高版本
Spring Boot 2.1.0-RELEASE
Maven 3+
点击最多、最新发布
数据格式定义
首先,我们把侧边栏中点击最多和最新发布两个栏目中的数据填充完成,数据填充之前,我们需要确认一下这两个栏目中的数据格式是怎样的,如下图中第四部分,这里以点击最多为例,通过图中的信息可以得出,这是一个博客列表,所以后端肯定会返回一个 List 格式的对象,展示的数据仅仅为博客标题,即哪些博客是点击量比较高的,同理,最新发布栏目中即为哪些博客是发布时间较新的。
虽然通过图片我们只能看出一个博客标题字段,但是这里通常会设计成可跳转的形式,即点击标题后会跳转到对应的博客详情页面中,因此还需要一个博客实体的 id 字段,因此返回数据的格式就得出来了,编码如下:
package com.site.blog.my.core.controller.vo;
import java.io.Serializable;
public class SimpleBlogListVO implements Serializable {
private Long blogId;
private String blogTitle;
public Long getBlogId() {
return blogId;
}
public void setBlogId(Long blogId) {
this.blogId = blogId;
}
public String getBlogTitle() {
return blogTitle;
}
public void setBlogTitle(String blogTitle) {
this.blogTitle = blogTitle;
}
}
数据查询实现
接下来是数据查询的功能实现,上述两种类型的博客列表都是可以通过直接查询 tb_blog 文章表来获取,只不过查询时使用到的字段不同,一个会使用到浏览量字段,一个是使用创建时间字段,实现逻辑如下。
首先,定义 service 方法,业务层代码如下(注:完整代码位于 com.site.blog.my.core.service.impl.BlogServiceImpl.java):
/**
* 首页侧边栏数据列表
* 0-点击最多 1-最新发布
*
* @param type
* @return
*/
public List getBlogListForIndexPage(int type) {
List simpleBlogListVOS = new ArrayList<>();
List blogs = blogMapper.findBlogListByType(type, 9);
if (!CollectionUtils.isEmpty(blogs)) {
for (Blog blog : blogs) {
SimpleBlogListVO simpleBlogListVO = new SimpleBlogListVO();
BeanUtils.copyProperties(blog, simpleBlogListVO);
simpleBlogListVOS.add(simpleBlogListVO);
}
}
return simpleBlogListVOS;
}
我们定义了 getBlogListForIndexPage() 方法并定义 type 参数,type 等于 0 时为查询点击最多的博客列表,type 等于 1 时为查询最新发布的博客列表,返回的数据格式为 SimpleBlogListVO,方法逻辑实现为:首先根据 type 字段的不同去查询对应的博客列表,但是查询出来的数据类型为 Blog,之后将 Blog 类型的数据转换为 SimpleBlogListVO 并返回即可。
具体的 SQL 语句如下(注:完整代码位于 resources/mapper/BlogMapper.xml):
select
from tb_blog
where is_deleted=0 AND blog_status = 1
order by blog_views desc
order by blog_id desc
limit #{limit}
数据渲染
想要将数据通过 Thymeleaf 语法渲染到前端页面上,首先需要将数据带过来,需要在首页请求的 Controller 方法中将查询到的数据放入 request 域中,代码修改如下:(注:完整代码位于 com.site.blog.my.core.controller.blog.MyBlogController.java)
@GetMapping({"/", "/index", "index.html"})
public String index(HttpServletRequest request) {
request.setAttribute("newBlogs", blogService.getBlogListForIndexPage(1));
request.setAttribute("hotBlogs", blogService.getBlogListForIndexPage(0));
request.setAttribute("pageName", "首页");
return "blog/index";
}
分别查出最新发布的博客列表和点击最多的博客列表并放入到 request 对象中,分别取名为 newBlogs 和 hotBlogs,之后跳转到 index 模板页面进行数据渲染。
index.html 文件修改如下:(注:完整代码位于 resources/templates/blog/index.html)
在点击最多栏目和最新发布栏目对应的位置读取 hotBlogs 和 newBlogs,并使用 th:each 循环语法将标题渲染出来,由于博客详情页面还没做,a 标签中的详情跳转链接暂时就不写了,之后会在博客详情页面功能完善后补上。
之后重启项目查看数据是否正常,如下图所示即为正确的侧边栏数据展示:
标签栏
数据格式定义
数据填充之前,我们需要确认一下数据格式是怎样的,如下图中第五部分,通过图中的信息可以得出,这是一个标签名称的列表,展示的数据为标签的名称,但是这样的话数据有些单薄,所以在这个原型图的基础上我又加上了每个标签对应的有多少篇文章在使用,因此这里显示的会是标签的名称和对应的博客数量两个字段。
当然这里通常也会设计成可跳转的形式,即点击标签后会跳转到对应的该标签下的博客列表中,因此还需要一个标签的主键字段,因此返回数据的格式就得出来了,编码如下:
package com.site.blog.my.core.entity;
public class BlogTagCount {
private Integer tagId;
private String tagName;
private Integer tagCount;
public Integer getTagId() {
return tagId;
}
public void setTagId(Integer tagId) {
this.tagId = tagId;
}
public String getTagName() {
return tagName;
}
public void setTagName(String tagName) {
this.tagName = tagName;
}
public Integer getTagCount() {
return tagCount;
}
public void setTagCount(Integer tagCount) {
this.tagCount = tagCount;
}
}
数据查询实现
通过前文中的数据格式定义我们也大致的清楚了我们需要查询的是什么数据,但是我们也不可能把所有的标签数据都查出来,因为数据量太大的话全部显示在页面会有些怪,所以标签栏的数据我设计成了查询当前使用最多的 20 个标签数据,这个数据的获取会比较复杂,因为标签和文章会涉及到三张表的操作:tb_blog 表、tb_blog_tag 表以及 tb_blog_tag_relation 表。
首先,定义 service 方法,业务层代码如下(注:完整代码位于 com.site.blog.my.core.service.impl.TagServiceImpl.java):
public List getBlogTagCountForIndex() {
return blogTagMapper.getTagCount();
}
我们定义了 getBlogTagCountForIndex() 方法,实现逻辑是直接返回 blogTagMapper.getTagCount() 执行后的返回数据,该方法其实并没有做什么逻辑,实现的难点都在 SQL 语句。
具体的 SQL 语句如下(注:完整代码位于 resources/mapper/BlogTagMapper.xml):
首先在 Mapper 文件中添加一个 ResultMap,代码如下:
之后是 getTagCount() 方法的 SQL 具体实现,代码如下:
SELECT t_r.*,t.tag_name FROM
(SELECT r.tag_id,r.tag_count FROM
(SELECT tag_id ,COUNT(*) AS tag_count FROM
(SELECT tr.tag_id FROM tb_blog_tag_relation tr LEFT JOIN tb_blog b ON tr.blog_id = b.blog_id WHERE b.is_deleted=0)
trb GROUP BY tag_id) r ORDER BY tag_count DESC LIMIT 20 ) AS t_r LEFT JOIN tb_blog_tag t ON t_r.tag_id = t.tag_id WHERE t.is_deleted=0
以上就是查询当前使用最多的 20 个标签数据的 SQL 语句,用到了连接查询以及聚合方法,看起来有些复杂,查询的层级也深,这里告诉大家该怎么更好的理解该 SQL,虽然查询层级深,在学习时也要一层一层的去执行和理解。
第一层 SQL:
SELECT tr.tag_id FROM tb_blog_tag_relation tr LEFT JOIN tb_blog b ON tr.blog_id = b.blog_id WHERE b.is_deleted=0
tb_blog_tag_relation 表和 tb_blog 表通过 blog_id 字段进行左连接查询,主要是为了过滤掉已删除博客记录的关联数据。
第二层 SQL:
SELECT tag_id ,COUNT(*) AS tag_count FROM
(SELECT tr.tag_id FROM tb_blog_tag_relation tr LEFT JOIN tb_blog b ON tr.blog_id = b.blog_id WHERE b.is_deleted=0) trb GROUP BY tag_id
直接根据第一层查询后的数据进行操作,并使用 GROUP BY tag_id 来进行数量统计,这一层 SQL 执行后返回的数据是标签的主键 tag_id 以及该主键下共有多少条关系数据。
第三层 SQL:
SELECT r.tag_id,r.tag_count FROM
(SELECT tag_id ,COUNT(*) AS tag_count FROM
(SELECT tr.tag_id FROM tb_blog_tag_relation tr LEFT JOIN tb_blog b ON tr.blog_id = b.blog_id WHERE b.is_deleted=0)
trb GROUP BY tag_id) r ORDER BY tag_count DESC LIMIT 20
直接根据第二层查询后的数据进行操作,主要是根据 tag_count 进行排序同时取出数量最多的 20 条数据。
第四层 SQL:
即是前文中写在 Mapper 文件中的 SQL 语句,这一层主要是对第三层查询后的数据与 tb_blog_tag 标签表做连接查询,把前一步查询出的 20 条记录的标签名称查出。
如果有同学对这个 SQL 恐惧的话可以按照十三给出的步骤去理解,当然,最好是按照十三给出的每一层的 SQL 去实际的查询,这样才能更好的理解这些查询的含义。
数据渲染
首先要在首页请求的 Controller 方法中将查询到的数据放入 request 域中,代码修改如下:(注:完整代码位于 com.site.blog.my.core.controller.blog.MyBlogController.java)
@GetMapping({"/", "/index", "index.html"})
public String index(HttpServletRequest request) {
request.setAttribute("newBlogs", blogService.getBlogListForIndexPage(1));
request.setAttribute("hotBlogs", blogService.getBlogListForIndexPage(0));
request.setAttribute("hotTags", tagService.getBlogTagCountForIndex());
request.setAttribute("pageName", "首页");
return "blog/index";
}
查出标签统计数据并放入到 request 对象中,对象命名为 hotTags,之后跳转到 index 模板页面进行数据渲染。
index.html 文件修改如下:(注:完整代码位于 resources/templates/blog/index.html)
```html