相信对于很多开发者来讲,树状结构的数据处理应该是最头疼的,那么到底如何在Spring Boot项目中来实现一个树状结构的数据的存储以及查询展示操作呢?下面我们就来一起看看。
具体实现
首先需要一个实体类来标识父子关系的结构,可以通过如下的方式来表示。
CREATE TABLE tree_node (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
parent_id BIGINT,
FOREIGN KEY (parent_id) REFERENCES tree_node(id)
);
按照这个库表结构来创建对应的实体类关系如下所示。这里需要注意,由于我们需要创建的实一个树结构,所以在创建实体类的时候需要考虑到其子结构如下所示。
package com.nh.bean;
import java.util.ArrayList;
import java.util.List;
public class TreeNode {
private Long id;
private String name;
private Long parentId;
private List children = new ArrayList<>();
// Getters and setters
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Long getParentId() {
return parentId;
}
public void setParentId(Long parentId) {
this.parentId = parentId;
}
public List getChildren() {
return children;
}
public void setChildren(List children) {
this.children = children;
}
}
编写Mapper接口和XML映射文件
接下来就是按照增删改查的要求来编写对应的Mapper映射代码如下所示。
package com.nh.dao;
import org.apache.ibatis.annotations.*;
import java.util.List;
@Mapper
public interface TreeNodeMapper {
@Select("SELECT * FROM tree_node WHERE id = #{id}")
TreeNode findById(Long id);
@Select("SELECT * FROM tree_node WHERE parent_id IS NULL")
List findRootNodes();
@Select("SELECT * FROM tree_node WHERE parent_id = #{parentId}")
List findByParentId(Long parentId);
@Insert("INSERT INTO tree_node(name, parent_id) VALUES(#{name}, #{parentId})")
@Options(useGeneratedKeys = true, keyProperty = "id")
void insert(TreeNode treeNode);
@Delete("DELETE FROM tree_node WHERE id = #{id}")
void deleteById(Long id);
}
创建mapper/TreeNodeMapper.xml映射文件,如下所示
INSERT INTO tree_node (name, parent_id)
VALUES (#{name}, #{parentId})
DELETE FROM tree_node WHERE id = #{id}
编写服务层逻辑
接下来就是创建业务逻辑层对象用来实现树结构的组装查询操作如下所示。
package com.nh.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
public class TreeNodeService {
@Autowired
private TreeNodeMapper treeNodeMapper;
@Transactional
public TreeNode save(TreeNode treeNode) {
treeNodeMapper.insert(treeNode);
return treeNode;
}
public TreeNode findById(Long id) {
return treeNodeMapper.findById(id);
}
public List findAll() {
List rootNodes = treeNodeMapper.findRootNodes();
for (TreeNode rootNode : rootNodes) {
loadChildren(rootNode);
}
return rootNodes;
}
private void loadChildren(TreeNode parentNode) {
List children = treeNodeMapper.findByParentId(parentNode.getId());
parentNode.setChildren(children);
for (TreeNode child : children) {
loadChildren(child);
}
}
public void deleteById(Long id) {
treeNodeMapper.deleteById(id);
}
}
在上面代码中构建树结构的过程中通过递归来完成树结构的构建。
编写控制器
创建一个Controller用来测试完成树结构的增删改查操作。
package com.nh.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/trees")
public class TreeNodeController {
@Autowired
private TreeNodeService treeNodeService;
@PostMapping
public TreeNode createTreeNode(@RequestBody TreeNode treeNode) {
return treeNodeService.save(treeNode);
}
@GetMapping("/{id}")
public TreeNode getTreeNode(@PathVariable Long id) {
return treeNodeService.findById(id);
}
@GetMapping
public List getAllTreeNodes() {
return treeNodeService.findAll();
}
@DeleteMapping("/{id}")
public void deleteTreeNode(@PathVariable Long id) {
treeNodeService.deleteById(id);
}
}
总结
到这里就完成了对于树结构增删改查操作,这里主要注意就如何在同一个表中去构建树状库表结构。也就是采用了一种递归的思想,当前节点既可以是某个节点的子节点,也是其他一部分节点的父节点,这样的一个设计思路。很多人在创建库表结构的时候就无法通过一个表来实现一个动态的树状结构。通过这个实现,帮助我们更好的理解如何通过一个表来实现树结构。