1.概述
评论回复功能是社交网站最基本的功能,本文主要讲解评论留言的设计及实现。
需求:
- 用户评论日记,回复评论
- 显示所有评论
2.数据库设计
日记表:diary
用户表:user
回复表:reply
字段设计
private int id ; //回复id (主键id) private Integer diaryId; //日记id (关联日记表) private String text; //回复内容 private Integer userId; //用户id (关联用户表) private Integer lastId; //上一条回复id (用于关联回复表,维护好每条评论的关系)
外键关系
讲解:
通过last_id我们就能找到这条回复是回复的哪条评论或日记。
3.java实现思路
- 存储我们使用链表,这样可以一步一步找到最后一条回复,应为一条评论下可能有多人回复,所以存储下一个对象我们使用List来存储(对象数组也行)
- 先查询last_id为null的数据,last_id为null则说明这是日记的第一层评论。
- 查询list_id不为null的数据,last_id不为null则说明这是回复。
- 通过last_id找到对应的评论,回复。添加到链表中。
- 打印
4.实现
示例数据库数据
示例显示(在web中显示)
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);
}
}
输出结果
应为没有用关联查询,所以没能显示谁回复的谁,但如果你已经能做到这里,相信对你来说也不是难事了、
(没有添加缩进符,所以显示的一排,但和示例显示的输出顺序是一样的)
4.总结
在这个demo中,使用了递归插入链表,递归查询链表。可能单看代码理解有些困难,但主要的是数据库设计,理解了数据库设计,你自己也能写出代码来。
在web程序中你可以将最终得到的list<Node>链表返回给前端,前端通过自己写一个js工具类,将list<Node>链表拆开显示。
可能还有一个更简单的方法,采用多对一的映射关系,让数据库通过外键关联关系自动帮我们添加链表,但是你拆开链表的时候还是要用到递归。因为现在对表的映射关系不是太懂,所以在这先不说。以后有机会在来分享。