1.概述

评论回复功能是社交网站最基本的功能,本文主要讲解评论留言的设计及实现。

需求:

  1. 用户评论日记,回复评论
  2. 显示所有评论

2.数据库设计

日记表:diary

用户表:user

回复表:reply

字段设计

评论回复功能后端存储在mongodb 评论回复数据库表设计_评论回复功能后端存储在mongodb


private int id ; //回复id (主键id) private Integer diaryId; //日记id (关联日记表) private String text; //回复内容 private Integer userId; //用户id (关联用户表) private Integer lastId; //上一条回复id (用于关联回复表,维护好每条评论的关系)


外键关系

评论回复功能后端存储在mongodb 评论回复数据库表设计_java_02

评论回复功能后端存储在mongodb 评论回复数据库表设计_评论回复功能后端存储在mongodb_03

讲解:

通过last_id我们就能找到这条回复是回复的哪条评论或日记。

3.java实现思路

  1. 存储我们使用链表,这样可以一步一步找到最后一条回复,应为一条评论下可能有多人回复,所以存储下一个对象我们使用List来存储(对象数组也行)
  2. 先查询last_id为null的数据,last_id为null则说明这是日记的第一层评论。
  3. 查询list_id不为null的数据,last_id不为null则说明这是回复。
  4. 通过last_id找到对应的评论,回复。添加到链表中。
  5. 打印

4.实现

示例数据库数据

评论回复功能后端存储在mongodb 评论回复数据库表设计_评论回复功能后端存储在mongodb_04

示例显示(在web中显示)

评论回复功能后端存储在mongodb 评论回复数据库表设计_评论回复功能后端存储在mongodb_05

dao层使用的jpa.

实体类

1.Reply(评论实体类)

package com.ss.entity;

import lombok.Data;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Data
@Table(name = "reply")
public class Reply {
    @Id
    @GeneratedValue
    private int id ; //回复id
    private Integer diaryId; //日记id
    private String text; //回复内容
    private Integer userId;  //用户id
    private Integer lastId;  //上一条回复id
}

2.Node(链表结构)

package com.ss.entity;

import lombok.Data;

import java.util.ArrayList;
import java.util.List;

@Data
public class Node {

    public Node(){

    }

    /**
     * 构造函数
     * @param reply
     */
    public Node(Reply reply){
        this.id = reply.getId();
        this.diaryId = reply.getDiaryId();
        this.lastId = reply.getLastId();
        this.text = reply.getText();
        this.userId = reply.getUserId();
    }


    private int id ;
    private Integer diaryId;
    private String text;
    private Integer userId;
    private Integer lastId;
    //下一条回复
    private List<Node> nextNodes = new ArrayList<Node>();


    /**
     * 将单个node添加到链表中
     * @param list
     * @param node
     * @return
     */
    public static boolean addNode(List<Node> list,Node node){
        for (Node node1 : list) {   //循环添加
            if (node1.getId()==node.getLastId()){   //判断留言的上一段是都是这条留言
               node1.getNextNodes().add(node);   //是,添加,返回true;
               System.out.println("添加了一个");
               return true;
            }else{     //否则递归继续判断
                if (node1.getNextNodes().size()!=0)
                    if (Node.addNode(node1.getNextNodes(),node)){
                        return true;
                    }
            }
        }
        return false;
    }

    /**
     * 将查出来的lastId不为null的回复都添加到第一层Node集合中
     * @param firstList
     * @param thenList
     * @return
     */
    public static List addAllNode(List<Node> firstList,List<Reply> thenList){
        while (thenList.size()!=0){
            int size = thenList.size();
            for (int i = 0;i<size;i++){
                if (Node.addNode(firstList,new Node(thenList.get(i)))){
                    thenList.remove(i);
                    i--;
                    size--;
                }
            }
        }
        return firstList;
    }

    //打印
    public static void show(List<Node> list){
        for (Node node : list) {
            System.out.println(node.getUserId()+" 用户回复了你:"+node.getText());
            if (node.getNextNodes().size()!=0){
                Node.show(node.getNextNodes());
            }
        }
    }


}

3.Test方法

package com.ss.reply;

import com.ss.dao.ReplyRepository;
import com.ss.entity.Node;
import com.ss.entity.Reply;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.ArrayList;
import java.util.List;

@RunWith(SpringRunner.class)
@SpringBootTest
public class ReplyApplicationTests {

    @Autowired
    private ReplyRepository replyRepository;

    @Test
    public void contextLoads() {
        //查询id为6且lastId为null的评论
        List firstList = replyRepository.findAllByDiaryIdAndLastIdNull(6);
        //查询id为6且LastId不为null的评论
        List thenList = replyRepository.findAllByDiaryIdAndLastIdNotNull(6);
        //新建一个Node集合。
        ArrayList<Node> nodes = new ArrayList<>();
        //将第一层评论都添加都Node集合中
        for (Object o : firstList) {
            nodes.add(new Node((Reply) o));
        }
        //将回复添加到对应的位置
        List list = Node.addAllNode(nodes, thenList);
        System.out.println();
        //打印回复链表
        Node.show(list);
    }

}

输出结果

评论回复功能后端存储在mongodb 评论回复数据库表设计_评论回复功能后端存储在mongodb_06

应为没有用关联查询,所以没能显示谁回复的谁,但如果你已经能做到这里,相信对你来说也不是难事了、

(没有添加缩进符,所以显示的一排,但和示例显示的输出顺序是一样的)

4.总结

在这个demo中,使用了递归插入链表,递归查询链表。可能单看代码理解有些困难,但主要的是数据库设计,理解了数据库设计,你自己也能写出代码来。

在web程序中你可以将最终得到的list<Node>链表返回给前端,前端通过自己写一个js工具类,将list<Node>链表拆开显示。

可能还有一个更简单的方法,采用多对一的映射关系,让数据库通过外键关联关系自动帮我们添加链表,但是你拆开链表的时候还是要用到递归。因为现在对表的映射关系不是太懂,所以在这先不说。以后有机会在来分享。