Springboot和Mybatis实现评论嵌套功能(一张表搞定哦)

一、首先我们需要设计comment表的字段

每个字段都包括了注释,这里就不再详细赘述。

二、对应的实体类属性

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 “评论效果图”)]

这里我们用th:each标签是可以列出每条评论下面的子评论的,但是我们该如何拿到这些子评论@符号后面的父评论呢,这就是我上文留下的悬念,自定义resultMap标签中的association property=“parentComment”,查询的方法和递归一样,每查询一次就要查询它的父评论和子评论,具体的代码可以查看上面的图片。这样我们就可以把这种无限的层级结构转换成这种双层结构,是不是看起来也很清晰明了
  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值