Spring boot 搭建个人博客系统(四)——文章的发布和分页显示

本文详细介绍使用SpringBoot、MyBatis和MySQL构建个人博客系统的过程,涵盖文章发布、Markdown编辑及分页显示等功能实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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类
@Mapper
public 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}">&laquo;</a>
    </li>
    #else
    <li class="disabled">
      <a href="">&laquo;</a>
    </li>
    #end
    <li><a href="">$!{pagination.current}/$!{pagination.lastPage}</a></li>
    #if($pagination.current < $pagination.lastPage)
    <li>
      <a href="/page/$!{pagination.nextPage}">&raquo;</a>
    </li>
    #else
    <li class="disabled">
      <a href="">&raquo;</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插件发布存储文章,并通过分页查询的方式将文章列表分页显示在主页上。

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.youkuaiyun.com/u013967175/article/details/77450152

核心功能 文章/图片/视频发布、喜欢、统计阅读次数。 文章标签tag功能、支持按tag分类 文章支持ueditor/markdown编辑器切换(后台配置) 评论功能,支持回复,支持表情。 第三方(微博、QQ)登录。 lucene实现的站内搜索。 响应式布局 支持用户订阅 先看效果图 SpringBoot开发非常美观的java博客系统(包含后台管理功能) SpringBoot开发非常美观的java博客系统(包含后台管理功能) SpringBoot开发非常美观的java博客系统(包含后台管理功能) http://localhost:8080/admin/group/list SpringBoot开发非常美观的java博客系统(包含后台管理功能) SpringBoot开发非常美观的java博客系统(包含后台管理功能)SpringBoot开发非常美观的java博客系统(包含后台管理功能) 技术选型: JDK8 数据库MySQL 主框架 (Spring-bootSpring-data-jpa) 安全权限 Shiro 搜索工具 Lucene 缓存 Ehcache 视图模板 Freemarker 其它 Jsoup、fastjson jQuery、Seajs Bootstrap 前端框架 UEditor/Markdown编辑器 font-Awesome 字体/图标 准备工作(sql文件在项目里面) 安装 Jdk8 安装 Maven 准备 IDE (如果你不看源码,可以忽略下面的步骤,直接通过Maven编译war包:mvn clean package -DskipTests) IDE 需要配置的东西 编码方式设为UTF-8 配置Maven 设置Jdk8 关于这些配置,网上有一大把的资料,所以此处不再重复。 获取代码导入到IDE 下载代码 导入到IDE的时候请选择以Maven的方式导入 项目配置参考 系统配置手册 配置完毕 启动项目,在控制台看到Mblog加载完毕的信息后,表示启动成功 打开浏览器输入:http//localhost/mblog/ (此处仅是示例,具体具体端口因人而异),访问成功即部署完毕 后台管理的地址是 /admin, 如果你是管理员账号点导航栏的头像会看到"后台管理" 启动成功后,你应该去后台的系统配置里配置你的网站信息等。 常见问题总结 进入系统后, 菜单加载不出来, 那应该是你没有导 db_init.sql 点标签显示乱码, 请设置Tomcat的 URIEncoding 为 UTF-8 项目截图 SpringBoot开发非常美观的java博客系统(包含后台管理功能) 转自:https://gitee.com/mtons/mblog SpringBoot开发非常美观的java博客系统(包含后台管理功能) 注意: 一、java main方式运行mblog-web下的BootApplication.java时抛出异常的解决方案 Unable to start EmbeddedWebApplicationContext due to missing EmbeddedServletContainerFactory bean. SpringBoot开发非常美观的java博客系统(包含后台管理功能) 注释掉后下面图片的这段后,记得maven要重新reimport SpringBoot开发非常美观的java博客系统(包含后台管理功能) SpringBoot开发非常美观的java博客系统(包含后台管理功能) 否则maven依赖不生效还是会抛出以上的异常 二、第三方登录点击后无响应,那是因为第三方开放平台回调的url失效导致,需要你去对应的第三方开放平台注册app后获取对应的oauth帐号 SpringBoot开发非常美观的java博客系统(包含后台管理功能) 三、idea以maven项目导入该项目后,发现没有maven的依赖包时,需要对每个maven module进行clearinstall,并且注意maven的依赖顺序 SpringBoot开发非常美观的java博客系统(包含后台管理功能) SpringBoot开发非常美观的java博客系统(包含后台管理功能) 、访问地址是http://localhost:8080 登录时,帐号,密码只要自己找个密码,然后md5下在更新到db中即可登录成功。 比如:zuidaima 111111,md5后密码是 3931MUEQD1939MQMLM4AISPVNE,md5的java类 SpringBoot开发非常美观的java博客系统(包含后台管理功能) SpringBoot开发非常美观的java博客系统(包含后台管理功能)
第1章 Spring Boot 简介 讲解Spring Boot的项目背景,已经与其他技术框架(比如,SpringSpringMVC、SpringCloud等)的关系。简单介绍下Spring Boot 整个生态系统 第2章 开启 Spring Boot 的第一个 Web 项目 通过 Spring Initializr 来快速初始化一个 Spring Boot 原型,方便学员来极速体验Spring Boot。本课程也将会采用Gradle作为项目管理工具,让学员掌握最前瞻的构建工具。通过探索项目让学员了解项目的结构,已经相关的配置原理。 第3章 一个Hello World项目 本章是正式开始动手敲代码了。依照惯例,会先编写一个最简单的Hello World程序。从项目配置,应用的编写,再到测试用例,最后运行项目。方面学员了解整个编码的流程。 第4章 开发环境的搭建 为了让实战过程更顺利,避免不要的问题,这里会先将课程所要求的环境进行一个讲解,并要求学员最好跟随课程的环境配置。本节也会讲解如何将项目导入IDE 来运行。 第5章 集成Thymeleaf模版引擎 Thymeleaf 方面的内容,知识点会讲解的相对全面点。Thymeleaf作为界面的模版引擎,对于界面的布局实现起着非常关键的作用。本章节也会讲解Thymeleaf 如何与 Spring Boot 来进行集成。最后通过一个实战,来让学员更加深刻的理解Thymeleaf。… 第6章 数据持久化Spring Data JPA 本章节涉及数据的持久化。从JPA规范讲起,到Spring对于JPA的用法以及与Hibernate集成实现。本课程的数据库采用MySQL,但也可以方便切换到其他数据库。最后通过一个实战内容,来帮助学员理解掌握。 第7章 全文搜索ElasticSearch 企业级应用中,难免会涉及到全文搜素。对于Java应用来说,ElasticSearch在全文搜索方面是一把“利器”。本章节会将带领学员了解全文搜索的概念,并熟悉如何用ElasticSearch来实现全文搜索。 第8章 架构设计与分层 本章节讲解了系统的整体架构设计思路,包括如何来组织项目结构。让学员理解系统的数据流程。 第9章 集成 Bootstrap Bootsrap最大的好处是,可以让整个系统界面实现响应式布局。本节先从Bootstrap 的基本原理讲起,并将常用的前端框架比如 JQuery等进行集成。最后通过一个实战内容,来帮助学员理解掌握。 第10章 博客系统的需求分析与原型设计 本章节是对博客系统的需求分析与设计。对于企业级应用的完整流程来说,需求的分析与设计是必不可少的环节。本章节设计部分包含了原型设计、数据库设计及接口设计。 第11章 权限管理Spring Security Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架,在企业级应用中被广泛使用。本章节不会对该框架做深入探讨,仅从基于角色的权限管理角度,来实现对系统的权限管理。 第12章 博客系统的整体框架实现 先对系统的整个界面、结构、布局、API进行实现,这样方便每个模块进行划分及实现。 第13章 博客系统用户管理实现 对用户管理模块进行前后台的实现。 第14章 博客系统的角色管理实现 对用户角色理模块进行前后台的实现。 第15章 博客系统的权限管理实现 对用权限理模块进行前后台的实现。 第16章 博客系统的博客管理实现 对博客管理模块进行前后台的实现。 第17章 博客系统的评论管理实现 对评论管理模块进行前后台的实现。 第18章 博客系统的点赞管理实现 对用户点赞理模块进行前后台的实现。 第19章 博客系统的分类管理实现 对分类管理模块进行前后台的实现。 第20章 博客系统的标签管理实现 对标签管理模块进行前后台的实现。 第21章 博客系统的搜索实现 对搜索模块进行前后台的实现。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值