一、项目场景:
当你做一个程序,需要实现评论功能时,如果只是单纯评论的功能,没有回复,这将会是非常简单的操作;如果需要有回复功能,那就麻烦了。在我的认知里,有回复型的评论写法有两种,一种是无限嵌套型的,它用到了递归,太消耗资源,这里不展开描述;其次就是今天要来讲的两层型
二、数据库实现:
要用到两层型的评论,表结构怎么实现呢,我看到好多人说要有两张表,一张评论表,一张回复表。其实不然,一张表就足够了,但是设置字段的时候,需要多设置一个parentId(这个初始化为-1,表示这个为父id)记录回复的评论id,方便日后寻找。就是这么简单,现在来看下表设计:
三、Java代码实现评论:
咱们在创建好评论表的实体类之后,在持久层编写插入语句(其实就是一个插入操作),然后来到控制层,这个控制层需要写两个请求(一个是评论请求,一个是回复评论请求),评论请求和回复请求的唯一区别就是,回复评论请求比评论请求多设置了一个parentId(前面说了,评论请求parentId都设置为-1),现在咱们看代码:
评论请求:
private void addCommon(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException { HttpSession session = req.getSession(); Users users = (Users) session.getAttribute(Constants.USER_SESSION_KEY); if (users != null){ String bookId = req.getParameter("bookId"); System.out.println("书id:"+bookId); Result result = bookService.findBookById(Integer.valueOf(bookId)); if (result.isFlag()){ Book book = (Book) result.getData(); if (book.getCommon()){ String content = req.getParameter("content"); content = new String(content.getBytes("iso-8859-1"),"utf-8"); // System.out.println("评论内容:"+content); Common common = new Common(); common.setBookId(Integer.valueOf(bookId)); common.setuId(users.getuId()); common.setContent(content); common.setParentId(-1); System.out.println("用户id:"+users.getuId()); commonService.addCommon(common); // req.getRequestDispatcher("/bookDetail.jsp").forward(req,resp); session.setAttribute("id",bookId); System.out.println(bookId); resp.sendRedirect("ok.jsp"); }else { req.setAttribute(Constants.REQUEST_MSG,"该书籍主人已关闭评论模块"); req.getRequestDispatcher("/bookDetail.jsp").forward(req,resp); } }else { req.setAttribute(Constants.REQUEST_MSG,"该书不存在"); req.getRequestDispatcher("/bookDetail.jsp").forward(req,resp); } }else { req.setAttribute(Constants.REQUEST_MSG,"您还未登录,请登录再评论"); req.getRequestDispatcher("/bookDetail.jsp").forward(req,resp); } }
回复评论请求
private void addReply(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { HttpSession session = req.getSession(); Users users = (Users) session.getAttribute(Constants.USER_SESSION_KEY); if (users != null){ String bookId = req.getParameter("bookId"); Result result = bookService.findBookById(Integer.valueOf(bookId)); System.out.println("------------------------i------------------------"); if (result.isFlag()){ Book book = (Book) result.getData(); if (book.getCommon()){ String parentId = req.getParameter("parentId"); String content = req.getParameter("content"+parentId); content = new String(content.getBytes("iso-8859-1"),"utf-8"); Common common = new Common(); common.setBookId(Integer.valueOf(bookId)); common.setuId(users.getuId()); common.setContent(content); common.setParentId(Integer.valueOf(parentId)); commonService.addCommon(common); // req.getRequestDispatcher("/bookDetail.jsp").forward(req,resp); System.out.println("ninh"+bookId); session.setAttribute("id",bookId); resp.sendRedirect("ok.jsp"); }else { req.setAttribute(Constants.REQUEST_MSG,"该书籍主任已关闭评论模块"); resp.sendRedirect("bookDetail.jsp"); } }else { req.setAttribute(Constants.REQUEST_MSG,"该书不存在"); resp.sendRedirect("bookDetail.jsp"); } }else { req.setAttribute(Constants.REQUEST_MSG,"您还未登录,请登录再评论"); resp.sendRedirect("bookDetail.jsp"); } }
这样评论就弄好了,是不是很easy,其实最难搞的还是查看评论这个功能,我这个项目前端不是很会弄,我就直接把查看评论这个功能和其他的功能合并了。
private void showDetail(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String id = req.getParameter("id"); Result result = bookService.showDetail(Integer.valueOf(id)); if (result.isFlag()){ req.setAttribute(Constants.REQUEST_MSG,result.getMessage()); req.setAttribute("book",(Book)result.getData()); // 获取评论信息 List<Common> commonList = commonService.findAll(((Book) result.getData()).getBookId()); List<Common> commons = new ArrayList<>(); Map<Integer, List<Common>> reply = new HashMap<>(); Map<Integer, String> userNames = new HashMap<>(); for (Common common : commonList) { if(userNames.get(common.getuId()) == null) { int uid=common.getuId(); Users users = (Users) userLoginService.showUserById(uid).getData(); userNames.put(uid,users.getUsername()); } if(common.getParentId() == -1) { commons.add(common); }else { if (reply.get(common.getParentId())==null){ reply.put(common.getParentId(),new ArrayList<Common>()); } reply.get(common.getParentId()).add(common); } } req.setAttribute("userNames",userNames); req.setAttribute("commons",commons); req.setAttribute("reply",reply); req.getRequestDispatcher("/bookDetail.jsp").forward(req,resp); } }
我这个评论功能在查看书籍详情下面直接评论(对于淘宝的点击商品页面后才能评论),先调用方法获取数据库的所有评论数据,设置三个容器(List 和Map):
List<Common> commons = new ArrayList<>(); Map<Integer, List<Common>> reply = new HashMap<>(); Map<Integer, String> userNames = new HashMap<>();
第一个list是装父评论(也就是parentId为-1的评论),它是所有子评论的“顶楼”,其次,reply就是装的子评论了,这个map的value是一个评论属性的List集合,就是为了装载全部的子评论,这里有一个点要注意,需要初始化List集合,我作了一个判断,如果reply.get(common.getParentId())==null,那就要初始化,第三个集合很明显就是存评论用户的username,确保每个评论前面都有用户名。
四、jsp代码演示:
评论和查看评论:
<c:if test="${book.getCommon()==true}"> <div class="contextForm"> <form action="./common.do?flag=addCommon&bookId=${book.bookId}" method="post"> <div class="title">留言:</div> <textarea rows="5" cols="200" name="content" required="required"></textarea> <br> <input type="submit" value="留言" /> <%-- <input type="hidden" >--%> <%-- <input type="hidden" name="flag" value="showDetail">--%> </form> </div> </c:if> <br> <h1>所有留言</h1> <hr> <c:if test="${!empty commons}"> <c:forEach items="${commons}" var="comment"> <div id="messDivId"> <div class="story"> <div class="opbtn"></div> <p class="story_t">${userNames.get(comment.uId)}</p> <p class="story_time">${comment.commonDate}</p> <p class="story_m">${comment.content}</p> <c:if test="${!empty requestScope.reply[comment.commonId]}"> <div class="story_hf"> <c:forEach items="${requestScope.reply[comment.commonId]}" var="reply"> <p>${requestScope.userNames[reply.uId]}: ${reply.content}</p> <p class="hf_time">${reply.commonDate}</p> </c:forEach> </div> </c:if> <div class='reply_textarea'> <form action="common.do" method='post'> <input type="hidden" name="flag" value="addReply"/> <input type="hidden" name="bookId" value="${book.bookId}"/> <input type="hidden" name="parentId" value="${comment.commonId}"/> <textarea name='content${comment.commonId}' rows='5' cols='200' required='required' ></textarea> <br> <input type='submit' value='回复' /> </form> </div> </div> <hr> </div> </c:forEach> </c:if>
五、展示页面
希望可以帮到你。觉得还行的朋友麻烦给个一键三连,谢谢。