一、首先我们需要设计comment表的字段
![](https://i-blog.csdnimg.cn/blog_migrate/175b1efcff8db6a31c7beb424324d746.jpeg)
每个字段都包括了注释,这里就不再详细赘述。
二、对应的实体类属性
public class Comment implements Serializable {
private Long id;
private String picture;
private String nickname;
private String email;
private String content;
private Date createTime;
private Blog blog;
private Long blogId;
private Comment parentComment;
private Boolean adminComment;
private List<Comment> replayComments;
private Long parentId;
}
三、使用mybatis将数据库中的各个数据取出来,这里我采用的是递归查询,无论评论的层级有多深,我们都可以用递归一层一层的查询到这些父评论的子评论,然后又是子评论的子评论。话不多说直接上代码
public interface CommentMapper{
List<Comment> selectCommentsByBlogId(
@Param("blogId") Long blogId,
@Param("pid") Long pid);
}
<?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.blog.mapper.CommentMapper">
<resultMap id="CommentWithChild" type="com.blog.pojo.Comment">
<id property="id" column="id"/>
<result property="nickname" column="nickname"/>
<result property="email" column="email"/>
<result property="content" column="content"/>
<result property="blogId" column="blog_id"/>
<result property="parentId" column="parent_id"/>
<result property="picture" column="picture"/>
<result property="createTime" column="create_time"/>
<result property="adminComment" column="admin_comment"/>
<association property="parentComment" javaType="com.blog.pojo.Comment"
select="com.blog.mapper.CommentMapper.selectCommentByParentId" column="{parentId=parent_id}">
</association>
<collection property="replayComments" ofType="com.blog.pojo.Comment"
select="com.blog.mapper.CommentMapper.selectCommentsByBlogId" column="{blogId=blog_id,pid=id}">
</collection>
</resultMap>
<select id="selectCommentByParentId" resultType="com.blog.pojo.Comment">
select id, nickname from comment where id = #{parentId}
</select>
<select id="selectCommentsByBlogId" resultMap="CommentWithChild">
select c.id,c.nickname,c.content,c.blog_id,c.parent_id,c.picture,
c.create_time,c.admin_comment from comment c
where c.blog_id = #{blogId} and c.parent_id = #{pid}
</select>
</mapper>
首先resultMap中包含了一个标签,对应的comment实体类属性为parentComment,照字面意思就是父评论,那么我们有了parentId的属性为什么还要加这个呢,先留一个悬念。
接下来才是重点,一个collection,对应的属性就是我们的子评论,这里我们和我们平时自定义的collection基本一致,就是多了两个属性,一个是select,一个是column,select就是我们递归查询的关键,column是用来给我们的递归查询传递参数的。
这段代码是service层查询评论和评论转换的代码
public List<Comment> ListCommentByBlogId(Long blogId) {
List<Comment> comments = commentMapper.selectCommentsByBlogId(blogId,-1L);
//将查询到的评论的各个级别都放在一层
return eachComment(comments);
}
/**
* 循环每个顶级的评论结点
* @param comments
* @return
*/
public List<Comment> eachComment(List<Comment> comments){
List<Comment> commentViews = new ArrayList<>();
for(Comment comment : comments){
Comment c = new Comment();
BeanUtils.copyProperties(comment,c);
commentViews.add(c);
}
//合并评论的各层子代到第一级子代集合中
combineChildren(commentViews);
return commentViews;
}
/**
* 对每个顶级结点的回复进行处理
* @param comments
*/
private void combineChildren(List<Comment> comments) {
for(Comment comment : comments){
List<Comment> replay = comment.getReplayComments();
if(replay.size()>0){
for(Comment replay1: replay){
//循环迭代,找出子代,存放在tempReplay中
recursively(replay1);
}
//修改顶级结点的replay集合为迭代处理后的集合
comment.setReplayComments(tempReplays);
}else{
comment.setReplayComments(null);
}
//清除临时存放区
tempReplays = new ArrayList<>();
}
}
//暂时存放迭代找出的所有子代的集合
private List<Comment> tempReplays = new ArrayList<>();
/**
* 递归迭代 ,剥洋葱
* @param replay
*/
private void recursively(Comment replay) {
tempReplays.add(replay);
if(replay.getReplayComments().size() > 0){
List<Comment> replays = replay.getReplayComments();
for(Comment replay1 : replays){
tempReplays.add(replay1);
if(replay.getReplayComments().size()>0){
recursively(replay1);
}
}
}
}
解释:我们先调用commentMapper#selectCommentsByBlogId()方法,当程序执行对应的xml文件中的select id="selectCommentsByBlogId"时会返回一个我们自定义的resultmap类型,这个时候collection标签的select作用就显现了,它会一直调用我们给它的select属性赋的值,然后我们传递多个参数需要用中括号包起来,等号左边就是我们mapper接口中传递的属性值保持一致,等号右边是我们定义的resultMap中需要传递的column的值。当我们拿到这些评论的集合之后就需要对它们进行转换了。
对这种转换总结一句话将查询的结果采用一个list重新封装,然后赋给我们的每一个comment对象,最后comment对象中的List就包括了所有的子评论了。
四、最后就是对页面使用thymeleaf做渲染效果如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8swrl9LL-1624444528531)(http://xupt-shopping-server.oss-cn-beijing.aliyuncs.com/myblog/1592921561628.png “评论效果图”)]