文章目录

  • 一、数据库设计
  • 二、搭建SpringBoot框架
  • 三、代码编写

零、效果
在网上搜索了很多,发现很多都是用两张表或者使用jpa实现的,本篇文章将讲述使用一张表来实现评论回复楼中楼功能,使用Mybatis作为持久层框架,有图有真相,先来看看最终效果

一、数据库设计

首先来看看有哪些字段,既然是评论回复,你觉得应该有哪些字段呢,带着功能去思考这个问题

首先是主键(id),既然是评论,必须要有评论人的姓名(nickname),为了以后能联系到评论人,需要评论人的邮箱(email),然后就是评论内容(content),为了方便显示,还需要评论人的头像(avatar),评论的时间当然少不了(create_time),既然是一张表,要如何确定回复的所属关系呢,那就需要知道是回复谁的评论(parent_comment_id)。bingo!这些字段就OK了,可能你会问,那回复怎么办,问题不大,可以在实体类中创建一个回复评论的集合replyComments,用来存储回复消息。

建表语句如下:

create database comment;DROP TABLE IF EXISTS `comment`;CREATE TABLE `comment` (`id` bigint(20) NOT NULL AUTO_INCREMENT,`nickname` varchar(255) DEFAULT NULL,`email` varchar(255) DEFAULT NULL,`content` varchar(255) DEFAULT NULL,`avatar` varchar(255) DEFAULT NULL,`create_time` datetime DEFAULT NULL,`parent_comment_id` bigint(20) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=19 DEFAULT CHARSET=utf8;

二、搭建SpringBoot框架

1.如图,选择相应的组件,创建SpringBoot项目

2.将application.properties配置文件改成yml后缀,即application.yml,主要对thymeleaf模板、数据库、mybatis、评论头像进行配置,配置如下:

spring:
#配置thymeleaf模板thymeleaf:mode: HTML
#配置数据库datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/comment?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTCusername: rootpassword: 806188#配置mybatis
mybatis:type-aliases-package: com.star.entitymapper-locations: classpath:mapper/*.xmlconfiguration:map-underscore-to-camel-case: true#配置评论头像
comment.avatar: /images/avatar.png

三、代码编写

  1. 实体类
    创建实体类,这里除了字段和回复评论列表外父级评论和父级评论姓名的变量,用来设置父级评论的id和显示父级评论姓名的
package com.star.entity;import java.util.ArrayList;
import java.util.Date;
import java.util.List;/*** @Description: 评论实体类* @Date: Created in 11:02 2020/4/14* @Author: ONESTAR* @QQ: 316392836* @URL: https://onestar.newstar.net.cn/*/
public class Comment {private Long id;private String nickname;private String email;private String content;private String avatar;private Date createTime;private Long parentCommentId;//回复评论private List<Comment> replyComments = new ArrayList<>();private Comment parentComment;private String parentNickname;//省去了get和set还有toString方法}
  1. 业务层接口
    创建业务层service接口,主要是查询评论列表和存储评论两个接口
package com.star.service;import com.star.entity.Comment;import java.util.List;/*** @Description: 评论业务层接口* @Date: Created in 11:48 2020/4/14* @Author: ONESTAR* @QQ: 316392836* @URL: https://onestar.newstar.net.cn/*/
public interface CommentService {//查询评论列表List<Comment> listComment();//保存评论int saveComment(Comment comment);}
  1. 持久层接口
    创建持久层dao接口,只要有添加评论、查询父级评论、查询一级回复、查询二级以及所有子集回复
package com.star.dao;import com.star.entity.Comment;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;import java.util.List;/*** @Description: 评论持久层接口* @Date: Created in 12:06 2020/4/14* @Author: ONESTAR* @QQ: 316392836* @URL: https://onestar.newstar.net.cn/*/
@Mapper
@Repository
public interface CommentDao {//添加一个评论int saveComment(Comment comment);//查询父级评论List<Comment> findByParentIdNull(@Param("ParentId") Long ParentId);//查询一级回复List<Comment> findParentIdNotNull(@Param("id") Long id);//查询二级以及所有子集回复List<Comment> findByReplayId(@Param("childId") Long childId);}
  1. Mapper
    创建CommentDao.xml文件,主要是添加评论、查询父级评论、查询一级回复、查询二级以及所有子集评论的SQL语句
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.star.dao.CommentDao"><!--添加评论--><insert id="saveComment" parameterType="com.star.entity.Comment">insert into comment.comment (nickname,email,content,avatar,create_time,parent_comment_id)values (#{nickname},#{email},#{content},#{avatar},#{createTime},#{parentCommentId});</insert><!--查询父级评论--><select id="findByParentIdNull" resultType="com.star.entity.Comment">select *from comment.comment cwhere c.parent_comment_id = #{ParentId}order by c.create_time desc</select><!--查询一级回复--><select id="findByParentIdNotNull" resultType="com.star.entity.Comment">select *from comment.comment cwhere c.parent_comment_id = #{id}order by c.create_time desc</select><!--查询二级以及所有子集回复--><select id="findByReplayId" resultType="com.star.entity.Comment">select *from comment.comment cwhere c.parent_comment_id = #{childId}order by c.create_time desc</select></mapper>
  1. 控制器CommentController
package com.star.controller;import com.star.entity.Comment;
import com.star.service.CommentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;import java.util.List;/*** @Description: 评论控制器* @Date: Created in 14:30 2020/4/14* @Author: ONESTAR* @QQ: 316392836* @URL: https://onestar.newstar.net.cn/*/
@Controller
public class CommentController {@Autowiredprivate CommentService commentService;@Value("${comment.avatar}")private String avatar;@GetMapping("/")public String comment() {return "comment";}@GetMapping("/comment")public String comments(Model model) {List<Comment> comments = commentService.listComment();model.addAttribute("comments", comments);return "comment :: commentList";}@PostMapping("/comment")public String post(Comment comment) {//设置头像comment.setAvatar(avatar);if (comment.getParentComment().getId() != null) {comment.setParentCommentId(comment.getParentComment().getId());}commentService.saveComment(comment);return "redirect:/comment";}
}
  1. 接口实现类
    逻辑都在接口实现类CommentServiceImpl里面实现,咱们来梳理一下逻辑(逻辑没梳理好,代码永远都是乱的,逻辑是第一步!!!),添加评论直接insert就可以了,这里主要讲一下查询:

1.根据id为“-1”查询出所有父评论
2.根据父评论的id查询出一级子回复
3.根据子回复的id循环迭代查询出所有子集回复
4.将查询出来的子回复放到一个集合中
5.要注意将父评论的姓名给set进去,代码如下:

package com.star.service.impl;import com.star.dao.CommentDao;
import com.star.entity.Comment;
import com.star.service.CommentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.util.ArrayList;
import java.util.Date;
import java.util.List;/*** @Description:* @Date: Created in 13:44 2020/4/14* @Author: ONESTAR* @QQ: 316392836* @URL: https://onestar.newstar.net.cn/*/
@Service
public class CommentServiceImpl implements CommentService {@Autowiredprivate CommentDao commentDao;//存放迭代找出的所有子代的集合private List<Comment> tempReplys = new ArrayList<>();/*** @Description: 查询评论* @Auther: ONESTAR* @Date: 17:26 2020/4/14* @Param:* @Return: 评论消息*/@Overridepublic List<Comment> listComment() {//查询出父节点List<Comment> comments = commentDao.findByParentIdNull(Long.parseLong("-1"));for(Comment comment : comments){Long id = comment.getId();String parentNickname1 = comment.getNickname();List<Comment> childComments = commentDao.findByParentIdNotNull(id);//查询出子评论combineChildren(childComments, parentNickname1);comment.setReplyComments(tempReplys);tempReplys = new ArrayList<>();}return comments;}/*** @Description: 查询出子评论* @Auther: ONESTAR* @Date: 17:31 2020/4/14* @Param: childComments:所有子评论* @Param: parentNickname1:父评论的姓名* @Return:*/private void combineChildren(List<Comment> childComments, String parentNickname1) {//判断是否有一级子回复if(childComments.size() > 0){//循环找出子评论的idfor(Comment childComment : childComments){String parentNickname = childComment.getNickname();childComment.setParentNickname(parentNickname1);tempReplys.add(childComment);Long childId = childComment.getId();//查询二级以及所有子集回复recursively(childId, parentNickname);}}}/*** @Description: 循环迭代找出子集回复* @Auther: ONESTAR* @Date: 17:33 2020/4/14* @Param: childId:子评论的id* @Param: parentNickname1:子评论的姓名* @Return:*/private void recursively(Long childId, String parentNickname1) {//根据子一级评论的id找到子二级评论List<Comment> replayComments = commentDao.findByReplayId(childId);if(replayComments.size() > 0){for(Comment replayComment : replayComments){String parentNickname = replayComment.getNickname();replayComment.setParentNickname(parentNickname1);Long replayId = replayComment.getId();tempReplys.add(replayComment);//循环迭代找出子集回复recursively(replayId,parentNickname);}}}@Override//存储评论信息public int saveComment(Comment comment) {comment.setCreateTime(new Date());return commentDao.saveComment(comment);}
}
  1. 前端页面comment.html
    最后是前端显示页面,这里采用semantic-ui作为UI组件,使用thymeleaf模板解析,记得在static文件夹下创建images文件夹,并放一张名为avatar.png的图片
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>评论楼中楼功能</title><link rel="stylesheet" href="https://cdn.jsdelivr.net/semantic-ui/2.2.4/semantic.min.css">
</head>
<body><div id="waypoint" class="m-margin- animated fadeIn"><div class="ui container m-opacity box-shadow-max"><div  class="ui bottom attached segment"><!--评论区域列表--><div id="comment-container"  class="ui teal segment"><div th:fragment="commentList"><div class="ui threaded comments" style="max-width: 100%;"><h3 class="ui dividing header">评论</h3><div class="comment" th:each="comment : ${comments}"><a class="avatar"><img src="../static/images/avatar.png" th:src="@{${comment.avatar}}"></a><div class="content"><a class="author" ><span th:text="${comment.nickname}">小红</span></a><div class="metadata"><span class="date" th:text="${#dates.format(comment.createTime,'yyyy-MM-dd HH:mm')}">Today at 5:42PM</span></div><div class="text" th:text="${comment.content}">愿你走出半生,归来仍是少年!</div><div class="actions"><a class="reply" data-commentid="1" data-commentnickname="Matt" th:attr="data-commentid=${comment.id},data-commentnickname=${comment.nickname}" onclick="reply(this)">回复</a></div></div><!--子集评论--><div class="comments" th:if="${#arrays.length(comment.replyComments)}>0"><div class="comment" th:each="reply : ${comment.replyComments}"><a class="avatar"><img src="../static/images/avatar.png" th:src="@{${reply.avatar}}"></a><div class="content"><a class="author" ><span th:text="${reply.nickname}">小白</span>&nbsp;<span th:text="|@ ${reply.parentNickname}|" class="m-teal">@ 小红</span></a><div class="metadata"><span class="date" th:text="${#dates.format(reply.createTime,'yyyy-MM-dd HH:mm')}">Today at 5:42PM</span></div><div class="text" th:text="${reply.content}">你也是!</div><div class="actions"><a class="reply" data-commentid="1" data-commentnickname="Matt" th:attr="data-commentid=${reply.id},data-commentnickname=${reply.nickname}" onclick="reply(this)">回复</a></div></div></div></div></div></div></div></div><div id="comment-form" class="ui form"><input type="hidden" name="parentComment.id" value="-1"><div class="field"><textarea name="content" placeholder="请输入评论信息..."></textarea></div><div class="fields"><div class="field m-mobile-wide m-margin-bottom-small"><div class="ui left icon input"><i class="user icon"></i><input type="text" name="nickname" placeholder="姓名" th:value="${session.user}!=null ? ${session.user.nickname}"></div></div><div class="field m-mobile-wide m-margin-bottom-small"><div class="ui left icon input"><i class="mail icon"></i><input type="text" name="email" placeholder="邮箱" th:value="${session.user}!=null ? ${session.user.email}"></div></div><div class="field  m-margin-bottom-small m-mobile-wide"><button id="commentpost-btn" type="button" class="ui teal button m-mobile-wide"><i class="edit icon"></i>发布</button></div></div></div></div></div>
</div><script src="https://cdn.jsdelivr.net/npm/jquery@3.2/dist/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/semantic-ui/2.2.4/semantic.min.js"></script><script th:inline="javascript">//评论表单验证$('.ui.form').form({fields: {title: {identifier: 'content',rules: [{type: 'empty',prompt: '请输入评论内容'}]},content: {identifier: 'nickname',rules: [{type: 'empty',prompt: '请输入你的大名'}]},type: {identifier: 'email',rules: [{type: 'email',prompt: '请填写正确的邮箱地址'}]}}});$(function () {$("#comment-container").load(/*[[@{/comment}]]*/"comment/");});$('#commentpost-btn').click(function () {var boo = $('.ui.form').form('validate form');if (boo) {console.log('校验成功');postData();} else {console.log('校验失败');}});function postData() {$("#comment-container").load(/*[[@{/comment}]]*/"",{"parentComment.id" : $("[name='parentComment.id']").val(),"nickname": $("[name='nickname']").val(),"email"   : $("[name='email']").val(),"content" : $("[name='content']").val()},function (responseTxt, statusTxt, xhr) {// $(window).scrollTo($('#goto'),0);clearContent();});}function clearContent() {$("[name='nickname']").val('');$("[name='email']").val('');$("[name='content']").val('');$("[name='parentComment.id']").val(-1);$("[name='content']").attr("placeholder", "请输入评论信息...");}function reply(obj) {var commentId = $(obj).data('commentid');var commentNickname = $(obj).data('commentnickname');$("[name='content']").attr("placeholder", "@"+commentNickname).focus();$("[name='parentComment.id']").val(commentId);$(window).scrollTo($('#comment-form'),500);}
</script>
</body>
</html>
  1. 目录结构

SpringBoot和Mybatis实现评论楼中楼功能(一张表搞定)相关推荐

  1. SpringBoot和Mybatis实现评论楼中楼功能(一张表搞定,含源码)

    原文链接:https://onestar.newstar.net.cn/blog/35 零.效果 在网上搜索了很多,发现很多都是用两张表或者使用jpa实现的,本篇文章将讲述使用一张表来实现评论回复楼中 ...

  2. 如何实现 楼中楼评论(盖楼)spring boot + mybatis 附完整代码

    如何实现 楼中楼评论(盖楼) 理想效果: 需求分析 1. 实现用户评论功能(CRUD)--> 这里称之为父级评论 2. 能够对父级评论做出回复--> 这里称之为一级评论 3. 能够对一级评 ...

  3. 如何实现一个楼中楼的评论系统

    文章内容由蚊子的博客进行发布,或许你想看看他其他的博客呢: https://www.xiabingbao.com/comments/2017/09/01/blog-comments.html 1. 实 ...

  4. 楼中楼评论php实现,如何实现一个楼中楼的评论系统

    多说,网易云跟帖等第三方评论系统无法长期维护更新,蚊子就自己实现一个楼中楼的评论系统 1. 实现前的思考 在经历过多说和网易云跟帖后,总算是下定决心自己要写一个评论系统了. 我们在使用的很多评论系统中 ...

  5. Golang 获取评论列表,以楼中楼的方式

    一 楼中楼 就是评论里面仍然有评论,类似于网易跟帖,能够让相关的内容聚集在一起. 二 基本实现方式 定义结构体 type Comment struct {Id uint64 `json:"i ...

  6. 如何实现一个楼中楼的评论系统?

    1. 实现前的思考 在经历过多说和网易云跟帖后,总算是下定决心自己要写一个评论系统了. 我们在使用的很多评论系统中,目前比较流行的就是楼中楼的方式了,比如百度贴吧,wordpress等等.在这以前,一 ...

  7. 一个带有楼中楼的评论系统数据库设置思路

    前言 有个需求,需要实现百度贴吧那样能评论帖子中某一楼的评论里的评论 分析 说起来有点拗口,其实这个评论系统分为4个部分: 主题(楼主发布的帖子) 直接返回楼主的评论(从帖): 直接回复2的评论: 直 ...

  8. php楼中楼论坛,论坛回帖楼层的实现

    在论坛类型的应用中,每个帖子下面的评论以及回复,都有顺序和从属关系. 下面就仅设计二级的评论场景 1楼 a  xxxxxxxxx 楼中楼  b : xxxxxxxxxxx 楼中楼  c 回复 b : ...

  9. 百度贴吧爬虫爬取楼中楼

    贴吧爬虫爬取楼中楼 实现的思路大致如下: 1.打开百度贴吧一个帖子的url https://tieba.baidu.com/p/4796371684 2.按F12-Network-点击楼层中的下一页, ...

最新文章

  1. postfix邮件服务器搭建
  2. c程序设计语言_习题8-4_重新实现c语言的库函数fseek(FILE*fp,longoffset,intorigin)
  3. android开发环境搭建教程
  4. Android项目出现main.xml编译出错和 出现main.out.xml无法编译的解决办法
  5. ux设计师怎样找同类产品_UX设计师UI设计师产品设计师和UX研究人员有何不同
  6. erlang分布式编程模型
  7. 编译QT出错 Basic XLib functionality test failed!
  8. linux php文件,Linux php文件安装目录在哪
  9. python selenium 小知识点整理笔记(更新中...)
  10. js判断是手机访问还是电脑访问,进行自动跳转
  11. httphandler java_java – 使用HTTPHandler上传文件
  12. IIS 405 Method Not Allowed
  13. win7装matlab教程,win7系统怎么安装matlab软件(图文教程)
  14. 解析|自动驾驶的核心技术是什么?
  15. Pollard Rho算法分解因数
  16. 编码,隐匿在计算机软硬件背后的语言读书笔记(作者序)
  17. 显示和隐藏一个div的问题
  18. 第3周学习:ResNet+ResNeXt
  19. EasyX 如何使用 Win32 控件
  20. 【计算机科学基础】度量前缀与性能指标

热门文章

  1. PATH_INFO模式是什么?如何解析?
  2. 上海亚商投顾:沪指尾盘快速反弹微幅收跌 6G概念大涨
  3. Echarts绘制水球图
  4. Google Earth Engine(GEE)——Landsat8 TOA 影像去云
  5. 曾国藩家书之劝学篇 摘录1
  6. python输入学号姓名输出自我介绍_请编写一个程序,使用字典存储学生信息,学生信息包括学号和姓名,请根据学生学号从小到大输出学生信息。_学小易找答案...
  7. windows中探测计算网络的MTU值,window中ping命令参数和Linux中ping命令参数
  8. [摘录]高效人士七习惯—知己知彼原则
  9. Spring MVC 跨域处理-立哥技术
  10. ASP利用数据表生成多级联动下拉列表框