springboot+thymeleaf+jpa博客多级评论展示案例
效果:
- 实体层
- 业务层:
- 控制层:
- 前端:
实体层
省略了get set方法
@Entity
@Table(name = "t_comment")
public class Comment {@Id@GeneratedValueprivate Long id;private String nickname;private String email;private String content;private String avatar;@Temporal(TemporalType.TIMESTAMP)private Date createTime;@ManyToOneprivate Blog blog;@OneToMany(mappedBy = "parentComment")private List<Comment> replyComments = new ArrayList<>();@ManyToOneprivate Comment parentComment;private boolean adminComment;
}
业务层:
@Service
public class CommentServiceImpl implements CommentService {@Autowiredprivate CommentRepository commentRepository;@Overridepublic List<Comment> listCommentByBlogId(Long blogId) {Sort sort = new Sort("createTime");List<Comment> comments = commentRepository.findByBlogIdAndParentCommentNull(blogId,sort);return eachComment(comments);}@Transactional@Overridepublic Comment saveComment(Comment comment) {Long parentCommentId = comment.getParentComment().getId();if (parentCommentId != -1) {comment.setParentComment(commentRepository.findOne(parentCommentId));} else {comment.setParentComment(null);}comment.setCreateTime(new Date());return commentRepository.save(comment);}/*** 循环每个顶级的评论节点* @param comments* @return*/private List<Comment> eachComment(List<Comment> comments) {List<Comment> commentsView = new ArrayList<>();for (Comment comment : comments) {Comment c = new Comment();BeanUtils.copyProperties(comment,c);commentsView.add(c);}//合并评论的各层子代到第一级子代集合中combineChildren(commentsView);return commentsView;}/**** @param comments root根节点,blog不为空的对象集合* @return*/private void combineChildren(List<Comment> comments) {for (Comment comment : comments) {List<Comment> replys1 = comment.getReplyComments();for(Comment reply1 : replys1) {//循环迭代,找出子代,存放在tempReplys中recursively(reply1);}//修改顶级节点的reply集合为迭代处理后的集合comment.setReplyComments(tempReplys);//清除临时存放区tempReplys = new ArrayList<>();}}//存放迭代找出的所有子代的集合private List<Comment> tempReplys = new ArrayList<>();/*** 递归迭代,剥洋葱* @param comment 被迭代的对象* @return*/private void recursively(Comment comment) {tempReplys.add(comment);//顶节点添加到临时存放集合if (comment.getReplyComments().size()>0) {List<Comment> replys = comment.getReplyComments();for (Comment reply : replys) {tempReplys.add(reply);if (reply.getReplyComments().size()>0) {recursively(reply);}}}}
}
控制层:
@Controller
public class CommentController {@Autowiredprivate CommentService commentService;@Autowiredprivate BlogService blogService;@Value("${comment.avatar}")//comment.avatar: /images/avatar.pngprivate String avatar;@GetMapping("/comments/{blogId}")public String comments(@PathVariable Long blogId, Model model) {model.addAttribute("comments", commentService.listCommentByBlogId(blogId));return "blog :: commentList";//<div th:fragment="commentList">}// <h3 class="ui dividing header">评论</h3>
// <div class="comment" th:each="comment : ${comments}">
// <a class="avatar">
// <img src="https://unsplash.it/100/100?image=1005" th:src="@{${comment.avatar}}">
// </a>
// <div class="content">
// <a class="author" >
// <span th:text="${comment.nickname}">Matt</span>@PostMapping("/comments")public String post(Comment comment, HttpSession session) {Long blogId = comment.getBlog().getId();comment.setBlog(blogService.getBlog(blogId));User user = (User) session.getAttribute("user");if (user != null) {comment.setAvatar(user.getAvatar());comment.setAdminComment(true);} else {comment.setAvatar(avatar);}commentService.saveComment(comment);return "redirect:/comments/" + blogId;}
}
前端:
<!--留言区域列表--><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="https://unsplash.it/100/100?image=1005" th:src="@{${comment.avatar}}"></a><div class="content"><a class="author" ><span th:text="${comment.nickname}">Matt</span><div class="ui mini basic teal left pointing label m-padded-mini" th:if="${comment.adminComment}">博主</div></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}">How artistic!</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="https://unsplash.it/100/100?image=1005" th:src="@{${reply.avatar}}"></a><div class="content"><a class="author" ><span th:text="${reply.nickname}"></span><div class="ui mini basic teal left pointing label m-padded-mini" th:if="${reply.adminComment}"></div> <span th:text="|@ ${reply.parentComment.nickname}|" class="m-teal"></span></a><div class="metadata"><span class="date" th:text="${#dates.format(reply.createTime,'yyyy-MM-dd HH:mm')}"></span></div><div class="text" th:text="${reply.content}">How artistic!</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 id="comment-form" class="ui form"><input type="hidden" name="blog.id" th:value="${blog.id}"><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>
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(/*[[@{/comments/{id}(id=${blog.id})}]]*/"comments/6");});$('#commentpost-btn').click(function () {var boo = $('.ui.form').form('validate form');if (boo) {console.log('校验成功');postData();} else {console.log('校验失败');}});function postData() {$("#comment-container").load(/*[[@{/comments}]]*/"",{"parentComment.id" : $("[name='parentComment.id']").val(),"blog.id" : $("[name='blog.id']").val(),"nickname": $("[name='nickname']").val(),"email" : $("[name='email']").val(),"content" : $("[name='content']").val()},function (responseTxt, statusTxt, xhr) {// $(window).scrollTo($('#comment-container'),500);clearContent();});}function clearContent() {$("[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);}
springboot+thymeleaf+jpa博客多级评论展示案例相关推荐
- SpringBoot的个人博客管理系统(毕业论文范文)
基于spring boot 的个人博客系统 摘要 随着社会上计算机行业的发展,记录知识的手段就不仅限于笔记,逐渐由纸质衍生到电子笔记,继而随着电子记录文档方式的多样性发展,大家将所学的知识进行回顾总 ...
- Springboot搭建个人博客系列
前言 为什么想要搭建这个博客? 我还记得,在大二寒假的某天,同往常一样的在家解决这某个bug,不停地问度娘,很巧的碰到了一个同行在他的博客中完美的记录了我的bug的解决方案,随后我又看了看他写的其他博 ...
- 基于SpringBoot的个人博客系统设计与实现
摘 要 作为计算机的学生,我们学习的方法是通过老师,书籍,论文等.对很多从事计算机方面的人来说,他们学习知识是通过官方文档,以及相关博客.现在知名博客网站有很多,比如CSDN,博客园,还有全球最知名的 ...
- 基于SpringBoot的个人博客系统
项目编号:BS-PT-042 该博客是基于SpringBoot + Mybatis + Thymeleaf 等技术实现的 Java 博客系统,页面美观.功能齐全.部署简单及完善的代码,做为毕设项目的话 ...
- SpringBoot+Vue+Mybatis-plus 博客(一):完成博客后台前端登录页面、后端登录接口
SpringBoot+Vue+Mybatis-plus 博客:个人博客介绍及效果展示 SpringBoot+Vue+Mybatis-plus 博客(一):完成博客后台前端登录页面.后端登录接口 Spr ...
- 基于 Vue 和 SpringBoot 实现的博客系统(附源码)
今天给大家分享一个基于 Vue 和 SpringBoot 实现的博客系统! 源码在文章结尾处,大家自行下载即可,我设置的免积分下载! 一.主要功能 1.前端 后台管理系统采用Vue开发. 文章模块,支 ...
- 教学妹开发springboot+vue的博客论坛系统,so esay
今天给大家介绍一个简单的系统. 基于springboot+vue的博客论坛系统,如果你想学习更多的项目源码,可以在文章的末尾领取源码资料. 当然前面已经开源了很多的项目源码,都是免费学习的: 1,挑战 ...
- (附源码)springboot掌上博客系统 毕业设计 063131
Springboot掌上博客系统的设计与实现 摘 要 掌上博客系统是当今网络的热点,博客技术的出现使得每个人可以零成本.零维护地创建自己的网络媒体,Blog站点所形成的网状结构促成了不同于以往社区的B ...
- SpringBoot+Vue+Mybatis-plus 博客(七):完成友链管理前后端对接
SpringBoot+Vue+Mybatis-plus 博客:个人博客介绍及效果展示 SpringBoot+Vue+Mybatis-plus 博客(一):完成博客后台前端登录页面.后端登录接口 Spr ...
最新文章
- centos 非root用户(普通用户)替换yum安装软件方法
- C语言的双向链表头插法和尾插法,指定节点删除
- 灵魂调参师被AutoGluon打爆,李沐:调得一手好参的时代要过去了
- asp.net数据格式的Format-- DataFormatString
- 撕掉伪善——用人话解释马云的996两次发言
- 部署DNS服务和管理DNS
- 2020北京智源大会 图神经网络专题 总结
- 如何把Linux工具里的“军刀”BusyBox移植到RT-Thread Smart?
- kubernetes集群使用GPU及安装kubeflow1.0.RC操作步骤
- this指向 - 总结
- 一个IO的传奇一生 (9) -- Noop和Deadline调度器
- 通过还款计划表监控还款异常
- 浅谈长尾理论--《Makers》读后感
- java 长整型long_C语言和java 长整型为何打印不同?
- MDIO接口FPGA代码
- 一文让你理解什么是shallow heap及retained heap
- PHP 把ofd格式文件转PDF,打开OFD格式文件及将OFD格式文件转换成PDF文件
- 下列签名无效: EXPKEYSIG CDFB5FA52007B954 Metasploit 解决办法
- 将xlsx文件转换成CSV文件方法
- vue中倒计时(日,时,分,秒)的计算和当前时间计时读秒
热门文章
- python无需修改是什么特性_用户编写的python程序无需修改就可以在不同的平台运行,是python的什么特征...
- mysql底层用什么写的_天天写order by,你知道Mysql底层如何执行吗?
- 大庆师范学院计算机系徐媛老师,大庆师范学院课程表(未添加英语课).xls
- android单片机蓝牙小车,手把手教你做蓝牙小车
- cpp加密php,常用的数据加密规则算法(php包含MD5和RSA)
- php篮球比赛,篮球数据API接口 - 【篮球比赛动画直播变化数据】API调用示例代码...
- 弹出确定_Redmi K30 Pro再剧透:弹出式全面屏,没有高刷
- 华为校园招聘Java机试题
- 【大数据教程】HDFS基本架构、基本命令、回收站机制和API操作
- 深圳内推 | 粤港澳大湾区数字经济研究院招聘NLP算法研究员(可实习)