用servlet实现无限评论功能
最近在写博客功能中的无限评论,比如知乎上的,我可以给根评论进行评论,也可以给子评论进行评论,下面是我做的一个简单的例子,分享给大家,希望能帮助各位,也希望可以指出缺点。
先上图,看功能
思路:
- 点击文章,获取文章的id,例如id为1
- 点击以后,查找文章id为1的全部评论,进行遍历(这里用到的是递归,方便观看,如上述,黄的为根评论,回复的是文章;绿的为子评论,回复的是根评论或者是其他子评论)
- 一开始刚发布文章肯定没有评论,所以,在最下面是发布评论,是对文章进行评论,使其有根评论,获取其根评论的id。
- 在有了根评论后,点击根评论后面的回复按钮,弹出一个回复的div,填写姓名和内容
- 点击弹出div的回复,将回复的信息添加到数据库,在进行2步骤,重新遍历;
- 点击子评论,同样可以出现回复的div,添加姓名和内容,点击回复按钮,在进行2步骤,遍历。
- 这样的缺点:遍历的话,消耗性能比较大(但我就想出来这一种,建议各位可以看看闭包表,这种评论相当于树结构,方便查找,但我脑子笨,没咋看明白)
评论表:
前端页面:
页面比较丑,我只是说功能,大家可以自己去写。
1 //这个是回复的div 2 <div class="response_div"></div>
这个是在js里动态生成的,只是有个父元素去装其他子元素
//评论的div,装根评论和子评论 评论: <div class="user_comments"></div>
//发表评论的div,为根评论
发表评论 <div> <form id="form" onsubmit="return false" action="###" method="post"> <input id="article_id" type="hidden" name="articleId" value="${requestScope.article.get("ArticleId")}" > <p>个人昵称:<input id="user_name" type="text" name="name"></p> <p> 通知邮箱:<input id="email" type="email" name="email"></p> <p>评论内容:<input id="content" type="text" name="content"></p> <input type="button" value="提交" onclick="ajaxSelectComment()"> </form> </div>
其他相关在js文件中:
首先文章里没有评论,那么我在发表评论以后,点击按钮,将评论的信息通过ajax提交到servlet中(这里一开始打算用form表单的action方法直接提交,但后来感觉用ajax好一些,个人想法)
//发表评论,先获取输入框的内容,将值作为参数传递给servlet,然后返回所有的评论 function ajaxSelectComment() { let articleId=$("#article_id").val(); let name=$("#user_name").val(); let email=$("#email").val(); let content=$("#content").val(); $.ajax({ url:"article?action=addComment", type:"POST", dataType: "json", data:{articleId,name,email,content}, success:function (result) { alert("提交成功"); console.log(result); showRootComment(result); }, error:function () { alert("请求失败"); } }); }
请求后,跳到servlet中
protected void addComment(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //获取对应文章的id,评论的姓名,内容,以及email Map<String, String[]> comment = req.getParameterMap();
//声明service层对象
ArticleService articleService= new ArticleServicesImpl();
//调用service层中的方法
articleService.addComment(Integer.parseInt(comment.get("articleId")[0]),comment.get("name")[0],comment.get("content")[0],comment.get("email")[0]);
//调用查询全部评论的方法,并将其转换成json格式(这里我将查询全部评论和转成JSON格式封装成了一个方法)
this.returnJson(resp,this.selectUserComment(comment)); }
//查询全部评论方法,将Map传入其中,然后获取对应文章的id
public List<Comment> selectUserComment(Map<String, String[]> parameterMap){
ArticleService articleService=new ArticleServicesImpl();
return articleService.selectComment(Integer.parseInt(parameterMap.get("articleId")[0]));
}
//将集合转成JSON格式
public void returnJson(HttpServletResponse resp ,Object object) throws ServletException, IOException{
//将传过来的数组转化为JSON
PrintWriter out = resp.getWriter();
Gson gson=new Gson();
String jsonComments = gson.toJson(object);//查找总的评论并且转化为数组
System.out.println(jsonComments);
out.write(jsonComments);
out.flush();
out.close();
}
showRootComment(result)方法:
function showRootComment(result){ let str=""; console.log(result); $(".user_comments").empty(); for (let i = 0; i <result.length ; i++) { console.log(result.length); if(result[i].responseId==0){//说明这个是根评论 str="<div class='rootResponse'>\n" +"<input type='hidden' name='commentId' value="+result[i].pid+">"+ "#"+i+"楼"+"评论时间:"+result[i].publishTime + " | " + result[i].commentName+" <button onclick='responseDiv($(this).prev().val(),$(this).prev())'>回复<button/>\n" + "<div>"+result[i].commentContent+ "</div>\n" + "</div><hr>"; $(".user_comments").append(str); //查询子评论 digui(result,result[i]); } } }
查询子评论方法:digui(result,result[i])
function digui(result,comment) { let str=''; console.log(result+"###"+comment); for (let i = 0; i <result.length ; i++) { console.log(comment.pid); if (result[i].responseId==comment.pid){//那么这个是评论的子id str="<div class='ziResponse'>\n" +"<input type='hidden' name='commentId' value="+result[i].pid+">"+ "评论时间:"+result[i].publishTime + " | " + result[i].commentName+"<button onclick='responseDiv($(this).prev().val(),$(this).prev())'>111回复<button/>\n" + "<div>"+result[i].commentContent+ "</div>\n" + "</div><hr>"; //将其子评论div添加到.user_comments div中
$(".user_comments").append(str);
console.log(str);
digui(result,result[i]);
} } }
最后一行递归的作用:在查找到第一个子评论后,查找是否有其他子评论回复当前的子评论,如果有,则继续遍历,如果没有,则跳过
到这的话,遍历的就ok了,下面我们看如何回复评论而不是回复文章
每次遍历,都会有一个回复的按钮
<button onclick='responseDiv($(this).prev().val(),$(this).prev())'>111回复<button/>
点击回复按钮,
$(this).prev().val()
为获取当前元素的同胞元素的值,获取其对应的评论的id
$(this).prev()
获取其对应的同胞元素
点击回复按钮,跳到 responseDiv($(this).prev().val(),$(this).prev())方法
function responseDiv(responseId,nowElement) {
//首先将.responsediv移除,因为在点击其他评论的回复时,其他评论的回复div将消失 $(".response").remove(); let str="<div class=\"response\">\n" + " <input type=\"text\" name=\"userName\" placeholder=\"请输入用户姓名\" id=\"user_comment_name\">\n" + " <input type=\"text\" name=\"userComment\" placeholder=\"请输入评论内容\" id=\"user_comment\">\n" + " <input type=\"button\" value=\"回复\" onclick=\"ajaxResponse("+responseId+")\">\n" + " </div>"; //将这个回复div添加进这个评论的div中 nowElement.parent().append(str); }
点击回复,跳到ajaxResponse方法中,找到回复responseId,
function ajaxResponse(responseId) { //点击回复后,将回复的div移除 $(this).prev().remove(); //获取回复对应的文章id,回复的姓名,回复的内容 let articleId=$("#article_id").val(); let name=$("#user_comment_name").val(); let content=$("#user_comment").val(); $.ajax({ url:"article?action=addUserComment", type:"POST", dataType: "json", data:{articleId,name,content,responseId}, success:function (result) { alert("提交回复成功"); //重新遍历 showRootComment(result); }, error:function () { alert("请求失败"); } }); }///
servlet添加子评论的方法
protected void addUserComment(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Map<String, String[]> userComment = req.getParameterMap(); articleService.addResponse(Integer.parseInt(userComment.get("articleId")[0]),userComment.get("name")[0],userComment.get("content")[0],Integer.parseInt(userComment.get("responseId")[0])); this.returnJson(resp,this.selectUserComment(userComment)); }
添加回复,首先出现一个回复的div,填写回复的信息,点击回复按钮,将回复的内容添加到数据库,然后返回所有的评论,继续遍历,思路基本上就是这样
service层方法:
/** * 添加的是根评论 * @param articleId * @param name * @param content * @param email */ @Override public void addComment(int articleId, String name, String content, String email) { articleDao.addComment(articleId,name,content,email); //先查找对应文章的评论有多少 long commentSum=articleDao.selectCommentsSum(articleId); //修改对应文章中评论的数量 articleDao.insertCommentsSum(commentSum,articleId); }
/** * 查找对应文章的评论 * @param articleId * @return */ @Override public List<Comment> selectComment(int articleId) { return articleDao.selectComment(articleId); }
/** * 添加对应文章的评论,子评论 * @param articleId * @param commentName * @param commentContent * @param responseId */ @Override public void addResponse(int articleId,String commentName,String commentContent,int responseId) { articleDao.addUserComment( articleId, commentName, commentContent,responseId); }
dao层方法:
/** *查找对应文章的全部评论 */ public List<Comment> selectComment(int articleId) { List<Comment> comments=null; String sql= "SELECT * FROM COMMENT WHERE Article_Id=?"; try { comments = queryRunner.query(sql, new BeanListHandler<Comment>(Comment.class), articleId); } catch (SQLException e) { e.printStackTrace(); } return comments; }
/** * 添加根评论 * @param articleId * @param name * @param content * @param email */ @Override public void addComment(int articleId, String name, String content, String email) { String sql="INSERT INTO COMMENT (Article_Id,CommentName,CommentContent,email) VALUES(?,?,?,?) "; Object[] parms={articleId,name,content,email}; try { queryRunner.update(sql,parms); } catch (SQLException e) { e.printStackTrace(); } }
/** * 添加子评论 * @param articleId * @param commentName * @param commentContent * @param responseId */ @Override public void addUserComment(int articleId,String commentName,String commentContent,int responseId) { String sql="INSERT INTO COMMENT (Article_Id,CommentName,CommentContent,ResponseId) VALUES(?,?,?,?)"; Object[] parms={articleId,commentName,commentContent,responseId}; try { queryRunner.update(sql,parms); } catch (SQLException e) { e.printStackTrace(); } }
这就是关于评论的所有操作,如果大家有疑惑或者更好的建议,在评论区留言,看到会立即回复!