前言
最近在开发一个小型个人博客,在数据库设计方面遇到了一些问题,这里做一个记录。
我设计的博客包含博客的基本信息
,包括标题
、内容
等,此外还包括博客的标签
、类型
和评论
。
实际效果如下:
设计
一条博客下面对应多条一级父评论
,父评论
下面又有子评论
;每一条评论都有父评论(如果为第一级评论,则父评论为空),也有子评论(子评论可为空)。两种评论都有共同的评论博客ID
。这里暂且忽略评论用户的ID等信息。
对象关系
一篇博客可能对应多条评论,一条评论只能对应一篇博客。(一对多)
一条父评论可能包含多条子评论,一条子评论只能对应一条父评论。(一对多)
上面关系里:
- 博客和评论(一对多)的关系可以在评论表中使用外键关联博客表的博客id主键
- 父评论和子评论(一对多)的关系可以在评论表中使用
注意:在企业开发中,在面对大量数据、表、并发的需求时,应当避免使用外键来关联表;关联关系应当在业务层进行处理与判断,详情可参考【IT老齐012】阿里开发规范解读:为啥禁用外键约束?,这里我为了简单明了,使用主键来关联。
关系图
这里放一个关系图,用来理解上述的对象之间的关系
难点:如何在持久层存储这种关系(评论),然后在业务层高效表示出来?
持久层最佳的实践是:评论表存储每一条评论信息,每条评论都有一个主键comment_id
,每一条评论使用外键parent_id
关联同一张表里的父评论的comment_id
,再用外键blog_id
关联博客表里的博客blog_id
主要方案:
- 采用树形结构(类似二叉树)
- 采用二维结构(只有第一层和第二层的嵌套)
这里我才用“二维结构”,主要原因还是实现简单。
注意:以下的实现介绍都是在“二维结构”的实现方式上设计的,树形结构也可用这个实现,只是在处理从数据库中读取评论时操作不同(代码中有体现)。
评论表:
comment_id | … | blog_id | parent_id | root_parent |
---|---|---|---|---|
1 | … | 6 | NULL | NULL |
2 | … | 6 | 1 | 1 |
3 | … | 6 | 2 | 1 |
4 | … | 6 | NULL | NULL |
这里我还添加了一个属性root_parent
,主要用来存储一条评论的最顶层的父评论的id
,而parent_id
用来存储直接关联的父评论。这么做的主要原因:
- 我的业务层采用的是二维结构,存储一个根节点id方便处理数据,提高效率。
- 未来需要删除评论时可以删除
comment_id=x
的评论以及所有root_parent=x
的子评论。
Java实体类
Blog
这里省略了无关的字段,只专注于博客、评论
@Data
public class Blog {
/** 博客自身属性 **/
private Integer blog_id; // 博客ID
private String title; // 标题
private String content; // 内容
/** 实体关系 **/
private List<Comment> comments = new ArrayList<>(); // 博客评论(一对多)
// 构造器 ...
}
Comment
这里省略了无关的字段,只专注于评论
@Data
public class Comment {
/** 自身属性 **/
private Integer comment_id; // 评论id
private String content; // 评论内容
/** 实体关系 **/
private Integer blog_id; // 这里考虑到实际情况下:因为blog本身可能很大,在前后端传输数据时会影