评论功能已经成为APP和网站开发中的必备功能。本文采用springboot+mybatis-plus框架,通过代码主要介绍评论功能的数据库设计和接口数据返回。我们返回的格式可以分三种方案,第一种方案是先返回评论,再根据评论id返回回复信息,第二种方案是将评论回复直接封装成一个类似于树的数据结构进行返回(如果数据对的话,可以根据评论分页),第三种方案是将所有数据用递归的SQL查出来,再把数据解析成树,返回结果
1 数据库表结构设计
表结构:
CREATE TABLE `comment` (
`id` bigint(18) NOT NULL AUTO_INCREMENT,
`parent_id` bigint(18) NOT NULL DEFAULT '0',
`content` text NOT NULL COMMENT '内容',
`author` varchar(20) NOT NULL COMMENT '评论人',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '评论时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4;
数据添加:
INSERT INTO `comment` (`id`, `parent_id`, `content`, `author`, `create_time`) VALUES (1, 0, '这是评论1', '吴名氏', '2023-02-20 17:11:16');
INSERT INTO `comment` (`id`, `parent_id`, `content`, `author`, `create_time`) VALUES (2, 1, '我回复了第一条评论', '吴名氏', '2023-02-20 17:12:00');
INSERT INTO `comment` (`id`, `parent_id`, `content`, `author`, `create_time`) VALUES (3, 2, '我回复了第一条评论的第一条回复', '吴名氏', '2023-02-20 17:12:13');
INSERT INTO `comment` (`id`, `parent_id`, `content`, `author`, `create_time`) VALUES (4, 2, '我回复了第一条评论的第二条回复', '吴名氏', '2023-02-21 09:23:14');
INSERT INTO `comment` (`id`, `parent_id`, `content`, `author`, `create_time`) VALUES (5, 0, '这是评论2', '吴名氏', '2023-02-21 09:41:02');
INSERT INTO `comment` (`id`, `parent_id`, `content`, `author`, `create_time`) VALUES (6, 3, '我回复了第一条评论的第一条回复的第一条回复', '吴名氏', '2023-02-21 09:56:27');
添加后的数据:
![](https://i-blog.csdnimg.cn/blog_migrate/74d9513ec41967297374b17c96b92f14.png)
2 方案一
方案一先返回评论列表,再根据评论id返回回复列表,以此循环,具体代码下文进行展示
2.1 控制层CommentOneController.java
/**
* 方案一
* @author wuKeFan
* @date 2023-02-20 16:58:08
*/
@Slf4j
@RestController
@RequestMapping("/one/comment")
public class CommentOneController {
@Resource
private CommentService commentService;
@GetMapping("/")
public List<Comment> getList() {
return commentService.getList();
}
@GetMapping("/{id}")
public Comment getCommentById(@PathVariable Long id) {
return commentService.getById(id);
}
@GetMapping("/parent/{parentId}")
public List<Comment> getCommentByParentId(@PathVariable Long parentId) {
return commentService.getCommentByParentId(parentId);
}
@PostMapping("/")
public void addComment(@RequestBody Comment comment) {
commentService.addComment(comment);
}
}
2.2 service类CommentService.java
/**
* service类
* @author wuKeFan
* @date 2023-02-20 16:55:23
*/
public interface CommentService {
List<Comment> getCommentByParentId(Long parentId);
void addComment(Comment comment);
Comment getById(Long id);
List<Comment> getList();
}
2.3 service实现类CommentServiceImpl.java
/**
* @author wuKeFan
* @date 2023-02-20 16:56:00
*/
@Service
public class CommentServiceImpl implements CommentService{
@Resource
private CommentMapper baseMapper;
@Override
public List<Comment> getCommentByParentId(Long parentId) {
QueryWrapper<Comment> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("parent_id", parentId);
return baseMapper.selectList(queryWrapper);
}
@Override
public void addComment(Comment comment) {
baseMapper.insert(comment);
}
@Override
public Comment getById(Long id) {
return baseMapper.selectById(id);
}
@Override
public List<Comment> getList() {
return baseMapper.selectList(new QueryWrapper<Comment>().lambda().eq(Comment::getParentId, 0));
}
}
2.4 数据库持久层类CommentMapper.java
/**
* mapper类
* @author wuKeFan
* @date 2023-02-20 16:53:59
*/
@Repository
public interface CommentMapper extends BaseMapper<Comment> {
}
2.5 实体类Comment.java
/**
* 评论表实体类
* @author wuKeFan
* @date 2023-02-20 16:53:24
*/
@Data
@TableName("comment")
public class Comment {
@TableId(type = IdType.AUTO)
private Long id;
private Long parentId;
private String content;
private String author;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date createTime;
}
2.6 使用Postman请求接口,查看返回数据
2.6.1 请求评论列表接口(本地的url为:http://localhost:8081/one/comment/ ,GET请求),请求结果如图
![](https://i-blog.csdnimg.cn/blog_migrate/0e2d9a7b7424c50ed0e6695aba9ebed7.png)
2.6.2 根据评论id(或者回复id)返回回复列表(本地url为:http://localhost:8081/one/comment/parent/1 ,GET请求),请求结果如图
![](https://i-blog.csdnimg.cn/blog_migrate/05442ddd5b8f214179ce12b5c90af9cf.png)
3 方案二
方案二采用的是将数据装到一个类似树的数据结构,然后返回,数据如果多的话,可以根据评论列表进行分页
3.1 控制层CommentTwoController.java
/**
* @author wuKeFan
* @date 2023-02-20 17:30:45
*/
@Slf4j
@RestController
@RequestMapping("/two/comment")
public class CommentTwoController {
@Resource
private CommentService commentService;
@GetMapping("/")
public List<CommentDTO> getAllComments() {
return commentService.getAllComments();
}
@PostMapping("/")
public void addComment(@RequestBody Comment comment) {
commentService.addComment(comment);
}
}
3.2 service类CommentService.java
/**
* service类
* @author wuKeFan
* @date 2023-02-20 16:55:23
*/
public interface CommentService {
void addComment(Comment comment);
void setChildren(CommentDTO commentDTO);
List<CommentDTO> getAllComments();
}
3.3 service实现类CommentServiceImpl.java
/**
* @author wuKeFan
* @date 2023-02-20 16:56:00
*/
@Service
public class CommentServiceImpl implements CommentService{
@Resource
private CommentMapper baseMapper;
@Override
public void addComment(Comment comment) {
baseMapper.insert(comment);
}
@Override
public List<CommentDTO> getAllComments() {
List<CommentDTO> rootComments = baseMapper.findByParentId(0L);
rootComments.forEach(this::setChildren);
return rootComments;
}
/**
* 递归获取
* @param commentDTO 参数
*/
@Override
public void setChildren(CommentDTO commentDTO){
List<CommentDTO> children = baseMapper.findByParentId(commentDTO.getId());
if (!children.isEmpty()) {
commentDTO.setChildren(children);
children.forEach(this::setChildren);
}
}
}
3.4 数据库持久层类CommentMapper.java
/**
* mapper类
* @author wuKeFan
* @date 2023-02-20 16:53:59
*/
@Repository
public interface CommentMapper extends BaseMapper<Comment> {
@Select("SELECT id, parent_id as parentId, content, author, create_time as createTime FROM comment WHERE parent_id = #{parentId}")
List<CommentDTO> findByParentId(Long parentId);
}
3.5 实体类CommentDTO.java
/**
* 递归方式实体类
* @author wuKeFan
* @date 2023-02-20 17:26:48
*/
@Data
public class CommentDTO {
private Long id;
private Long parentId;
private String content;
private String author;
private List<CommentDTO> children;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date createTime;
}
3.6 使用Postman请求接口,查看返回数据
3.6.1 通过递归的方式以树的数据结构返回(本地url为:http://localhost:8081/two/comment/ ,GET请求),请求结果如图
![](https://i-blog.csdnimg.cn/blog_migrate/317abfa12b3ac5228efe3552f95d4433.png)
返回的json格式如图:
[
{
"id": 1,
"parentId": 0,
"content": "这是评论1",
"author": "吴名氏",
"children": [
{
"id": 2,
"parentId": 1,
"content": "我回复了第一条评论",
"author": "吴名氏",
"children": [
{
"id": 3,
"parentId": 2,
"content": "我回复了第一条评论的第一条回复",
"author": "吴名氏",
"children": [
{
"id": 6,
"parentId": 3,
"content": "我回复了第一条评论的第一条回复的第一条回复",
"author": "吴名氏",
"children": null,
"createTime": "2023-02-21 09:56:27"
}
],
"createTime": "2023-02-20 17:12:13"
},
{
"id": 4,
"parentId": 2,
"content": "我回复了第一条评论的第二条回复",
"author": "吴名氏",
"children": null,
"createTime": "2023-02-21 09:23:14"
}
],
"createTime": "2023-02-20 17:12:00"
}
],
"createTime": "2023-02-20 17:11:16"
},
{
"id": 5,
"parentId": 0,
"content": "这是评论2",
"author": "吴名氏",
"children": null,
"createTime": "2023-02-21 09:41:02"
}
]
4 方案三
方案三是将所有数据用递归的SQL查出来,再把数据解析成树,返回结果,适合数据较少的情况下,且MySQL版本需要在8.0以上
4.1 控制层CommentThreeController.java
/**
* @author wuKeFan
* @date 2023-02-20 17:30:45
*/
@Slf4j
@RestController
@RequestMapping("/three/comment")
public class CommentThreeController {
@Resource
private CommentService commentService;
@GetMapping("/")
public List<CommentDTO> getAllCommentsBySql() {
return commentService.getAllCommentsBySql();
}
@PostMapping("/")
public void addComment(@RequestBody Comment comment) {
commentService.addComment(comment);
}
}
4.2 service类CommentService.java
/**
* service类
* @author wuKeFan
* @date 2023-02-20 16:55:23
*/
public interface CommentService {
List<CommentDTO> getAllCommentsBySql();
}
4.3 service实现类CommentServiceImpl.java
/**
* @author wuKeFan
* @date 2023-02-20 16:56:00
*/
@Service
public class CommentServiceImpl implements CommentService{
@Resource
private CommentMapper baseMapper;
/**
* 递归获取
* @param commentDTO 参数
*/
@Override
public void setChildren(CommentDTO commentDTO){
List<CommentDTO> children = baseMapper.findByParentId(commentDTO.getId());
if (!children.isEmpty()) {
commentDTO.setChildren(children);
children.forEach(this::setChildren);
}
}
}
4.4 数据库持久层类CommentMapper.java
/**
* mapper类
* @author wuKeFan
* @date 2023-02-20 16:53:59
*/
@Repository
public interface CommentMapper extends BaseMapper<Comment> {
@DS("localhost80")
List<CommentDTO> getAllCommentsBySql();
}
4.5 实体类Comment.java
/**
* 评论表实体类
* @author wuKeFan
* @date 2023-02-20 16:53:24
*/
@Data
@TableName("comment")
public class Comment {
@TableId(type = IdType.AUTO)
private Long id;
private Long parentId;
private String content;
private String author;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date createTime;
private Integer level;
}
4.6 使用Postman请求接口,查看返回数据
4.6.1 通过递归的方式以树的数据结构返回(本地url为:http://localhost:8081/three/comment/ ,GET请求),请求结果如图
![](https://i-blog.csdnimg.cn/blog_migrate/929ae0960b261224875e5521dd1c24da.png)
返回的json格式如图:
[
{
"id": 1,
"parentId": 0,
"content": "这是评论1",
"author": "吴名氏",
"children": [
{
"id": 2,
"parentId": 1,
"content": "我回复了第一条评论",
"author": "吴名氏",
"children": [
{
"id": 3,
"parentId": 2,
"content": "我回复了第一条评论的第一条回复",
"author": "吴名氏",
"children": [
{
"id": 6,
"parentId": 3,
"content": "我回复了第一条评论的第一条回复的第一条回复",
"author": "吴名氏",
"children": [],
"createTime": "2023-02-21 09:56:27",
"level": 4
}
],
"createTime": "2023-02-20 17:12:13",
"level": 3
},
{
"id": 4,
"parentId": 2,
"content": "我回复了第一条评论的第二条回复",
"author": "吴名氏",
"children": [],
"createTime": "2023-02-21 09:23:14",
"level": 3
}
],
"createTime": "2023-02-20 17:12:00",
"level": 2
}
],
"createTime": "2023-02-20 17:11:16",
"level": 1
},
{
"id": 5,
"parentId": 0,
"content": "这是评论2",
"author": "吴名氏",
"children": [],
"createTime": "2023-02-21 09:41:02",
"level": 1
}
]
5 总结
以上三种方案各有优缺点,需要从不同场景中使用不同的方案