Spring boot 搭建个人博客系统(四)——文章的发布和分页显示
来源:互联网 发布:java面向对象编程 编辑:程序博客网 时间:2024/05/17 08:13
Spring boot 搭建个人博客系统(四)——文章的发布和分页显示
一直想用Spring boot 搭建一个属于自己的博客系统,刚好前段时间学习了叶神的牛客项目课受益匪浅,乘热打铁也主要是学习,好让自己熟悉这类项目开发的基本流程。系统采用Spring boot+MyBatis+MySQL的框架进行项目开发。
项目源码:Jblog
个人主页:tuzhenyu’s page
原文地址:Spring boot 搭建个人博客系统(四)——文章的发布和分页显示
0. 思路
- 文章的发布是将文章内容以及文章信息插入到数据库相应的字段中。为了后续文章的显示,在发布文章时需要插入数据库文章标题(title),文章的描述(describe),文章的内容(content),文章的创建时间(create_date),文章的评论数目(commet_count)和文章的类别(category)等。为了文章编辑的便利引入Markdown编辑页面以及Markdown解析包将输入的Markdown语法的文本解析成相应的HTML文本插入数据库中。
- 文章的显示是按照日期降序从数据库中取出文章,在主页按照发布时间早晚显示文章列表,在文章显示页面显示具体文章的内容。
- 文章的分页显示是为减少页面显示的延迟,因为如果在页面上显示数据库中的所有文章,则所需的查询显示时间较长,降低了用户的体验。
1. 数据模型
文章的发布和显示功能需要操作数据库中的article表,使用MyBatis作为系统的ORM框架用来简化数据操作。
- 添加数据库表实体类
public class Article { private int id; private String title; private String describes; private String content; private Date createdDate; private int commentCount; private String category; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getDescribes() { return describes; } public void setDescribes(String describes) { this.describes = describes; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } public Date getCreatedDate() { return createdDate; } public void setCreatedDate(Date createdDate) { this.createdDate = createdDate; } public int getCommentCount() { return commentCount; } public void setCommentCount(int commentCount) { this.commentCount = commentCount; } public String getCategory() { return category; } public void setCategory(String category) { this.category = category; }}
- 添加数据库操作DAO类
@Mapperpublic interface ArticleDao { String TABLE_NAEM = " article "; String INSERT_FIELDS = " title, describes, content, created_Date, comment_Count, category "; String SELECT_FIELDS = " id, " + INSERT_FIELDS; @Insert({"insert into",TABLE_NAEM,"(",INSERT_FIELDS,") values (#{title},#{describes},#{content}" + ",#{createdDate},#{commentCount},#{category})"}) int insertArticle(Article article); @Select({"select",SELECT_FIELDS,"from",TABLE_NAEM,"where id=#{id}"}) Article selectById(int id); @Select({"select",SELECT_FIELDS,"from",TABLE_NAEM,"order by id desc limit #{offset},#{limit}"}) List<Article> selectLatestArticles(@Param("offset") int offset, @Param("limit") int limit); @Select({"select",SELECT_FIELDS,"from",TABLE_NAEM,"where category=#{category} order by id desc limit #{offset},#{limit}"}) List<Article> selecttArticlesByCategory(@Param("category") String category,@Param("offset") int offset, @Param("limit") int limit); @Select({"select count(id) from",TABLE_NAEM,"where category=#{category}"}) int getArticleCountByCategory(@Param("category") String category); @Select({"select count(id) from",TABLE_NAEM}) int getArticleCount(); @Update({"update",TABLE_NAEM,"set comment_count = #{commentCount} where id = #{questionId}"}) void updateCommentCount(@Param("questionId") int questionId,@Param("commentCount") int commentCount); @Delete({"delete from",TABLE_NAEM,"where id=#{id}"}) void deleteById(int id);}
2. 文章的发布
(1) 为了文章编辑的便利,添加Editor.md插件,在文章编辑页引入Markdown编辑器。
<form action="/articleAdd" method = "post"> <div class="title option"><label>Title</label><input type="text" class="form-control" id="title" name="title"></div> <div class="row categoryTag"> <div class="col-md-6"><div><label>Category</label></div> <select class="" class="form-control" id="category" name="category"> <option value="Java">Java</option> <option value="Web">Web</option> <option value="Linux">Linux</option> <option value="分布式系统">分布式系统</option> <option value="数据库">数据库</option> <option value="算法">算法</option> <option value="其它">其它</option> </select> </div> <div class="col-md-6"><label>Tag</label><input type="text" class="form-control" id="tag" name="tag"></div> </div> <div class="describe option"><label>Describe</label><input type="text" class="form-control" id="describe" name="describe"></div> <div class="option"><label>Content</label></div> <div class="row"> <div id="test-editormd"> <textarea id="content" name="content" style="display:none;"></textarea> </div> </div> <div class="form-group articleSubmit option"> <button type="submit" class="btn btn-default">Submit</button> </div></form>
editormd("test-editormd", { width : "90%", height : 640, syncScrolling : "single", path : "<%=request.getContextPath()%>/resources/editormd/lib/", saveHTMLToTextarea : true});
(2) 为了解析输入的Markdown语法的文本,添加flexmark-java插件,将Markdown语法的文本解析成能够直接显示的HTML文本,将文章内容和文章相关信息保存在数据库中。
<dependency> <groupId>com.vladsch.flexmark</groupId> <artifactId>flexmark-all</artifactId> <version>0.26.4</version></dependency>
@RequestMapping("/articleAdd") public String addArticle(@RequestParam("title")String title,@RequestParam("category")String category, @RequestParam("tag")String tag,@RequestParam("describe")String describe, @RequestParam("content")String content){ Article article = new Article(); article.setTitle(title); article.setDescribes(describe); article.setCreatedDate(new Date()); article.setCommentCount(0); article.setContent(JblogUtil.tranfer(content)); article.setCategory(category); int articleId = articleService.addArticle(article); return "redirect:/";}
public static String tranfer(String content){ MutableDataSet options = new MutableDataSet(); Parser parser = Parser.builder(options).build(); HtmlRenderer renderer = HtmlRenderer.builder(options).build(); Node document = parser.parse(content); return renderer.render(document);}
3. 文章的分页显示
(1) 为了加快页面显示,在文章列表显示的时候采用分页查询的方式,只查询显示一部分文章。考虑到数据库中文章的量级,系统的分页策略采用最简单的offset+limit的方式。
@RequestMapping(path = {"/","/index"}) public String index(Model model){ List<ViewObject> vos = new ArrayList<>(); List<Article> articles = articleService.getLatestArticles(0,4); for (Article article:articles){ ViewObject vo = new ViewObject(); vo.set("article",article); vos.add(vo); } ViewObject pagination = new ViewObject(); int count = articleService.getArticleCount(); User user = hostHolder.getUser(); if (user==null||"admin".equals(user.getRole())){ model.addAttribute("create",1); }else { model.addAttribute("create",0); } pagination.set("current",1); pagination.set("nextPage",2); pagination.set("lastPage",count/4+1); model.addAttribute("pagination",pagination); return "index";}
(2) 主页文章列表的显示
<ul class="articles"> #foreach($vo in $vos) <li class="blogAticle"> <div class="articleHeader"> <p><a href="/article/$!{vo.article.id}">$!{vo.article.title}</a></p> </div> <div class="articleContent"> <p>$!{vo.article.describes}</p> </div> <div class="articleFooter"> <ul> <li><i class="fa fa-calendar" aria-hidden="true"></i><span>$date.format('yyyy-MM-dd', $!{vo.article.createdDate})</span></li> <li><i class="fa fa-eye" aria-hidden="true"></i><span>$!{vo.clickCount}</span></li> <li><i class="fa fa-list" aria-hidden="true"></i><span>$!{vo.article.category}</span></li> <li><i class="fa fa-tags" aria-hidden="true"></i> #foreach($tag in $vo.tags) <span>$!{tag.name}</span> #end </li> <li class="readMore"><a href="/article/$!{vo.article.id}">read more</a></li> </ul> </div> </li> #end</ul><div class="paginationWapper"> <ul class="pagination"> #if($pagination.current > 1) <li> <a href="/page/$!{pagination.prePage}">«</a> </li> #else <li class="disabled"> <a href="">«</a> </li> #end <li><a href="">$!{pagination.current}/$!{pagination.lastPage}</a></li> #if($pagination.current < $pagination.lastPage) <li> <a href="/page/$!{pagination.nextPage}">»</a> </li> #else <li class="disabled"> <a href="">»</a> </li> #end </ul></div>
4. 分页查询
分页查询有很多实现的方案,在数据库数据量不大的情况下,各种方案的性能相差不大,但当数据库数据量到达一定量级之后,不同的查询方案的分页查询性能相差较大。
- 最基本的分页方式:
SELECT * FROM articles order by id desc LIMIT 50, 10
limit 子句的优点很明显,简单好用。缺点平时不显著,数据量一大就暴露了。数据库会完整扫描 offset 的行,然后继续扫描 200 行之后才把结果集返回。 offset 在 400W 的时候,这样的 SQL 做一次分页查询就已经至少耗时 5s 了。
- 子查询的分页方式:
select * from table where id in (select id from table order by id limit #offset#, #size#)
利用子查询分页的语句的效率在 offset 达到百万级时相比直接 limit 有数倍的提升,但是这条语句不但没有避免遍历 offset,还做了大量的无用重复工作。
- 主键范围分页方式:
select * from table where id >= #minId# limit 200
通过直接计算出主键的起始值,往后获取200条记录,这条语句执行效率较高,无需遍历offset条记录,但是要求通过主键查询。
5. 总结
利用Markdown插件发布存储文章,并通过分页查询的方式将文章列表分页显示在主页上。
- Spring boot 搭建个人博客系统(四)——文章的发布和分页显示
- Spring boot 搭建个人博客系统(六)——文章点击量和阅读排行榜
- Spring boot 搭建个人博客系统(五)——标签和标签云
- Spring boot 搭建个人博客系统(一)——整体思路
- Spring boot 搭建个人博客系统(二)——登录注册功能
- Spring boot 搭建个人博客系统(三)——权限管理功能
- django 个人博客系统开发 - 最新文章获取和分页
- 如何发布新文章到hexo搭建的个人博客
- 如何发布新文章到hexo搭建的个人博客
- 使用Spring Boot搭建个人博客全记录
- 首页数据分页【springboot mybatis个人博客系统(四)】
- flask搭建个人博客(四)——前端模板
- 用flask开发个人博客(36)—— 使用SQLAlchemy对博客文章进行分页
- django搭建个人博客06,发布文章页
- 如何使用hugo搭建个人博客(四):添加评论系统disqus
- django 个人博客系统开发 - 文章页面和自定义过滤器
- 快速搭建个人博客系统
- Spring-Boot构建博客系统
- 安全组规则批量添加授权对象
- discuz 地区下拉框联动用方法
- spring实战-Spring-security权限认证白名单
- 四大组件之ContentProvider
- Windows ToolTips简要介绍
- Spring boot 搭建个人博客系统(四)——文章的发布和分页显示
- 剑指offer_二叉树---二叉搜索树的第k个结点
- 欢迎使用CSDN-markdown编辑器
- python 数据结构三 之 栈与队列
- 使用jquery.lazyload.js 图片懒加载
- 乘法表
- 设计模式学习
- Spring中使用FastJson返回json数据
- 杭电暑期多校集训—Killer Names