非二叉树(无自然排序)节点插入问题(Java Spring Boot+MongoDB)
实现指定父节点的场景节点插入
针对你的Spring Boot+MongoDB冒险故事应用,要完成新节点挂载到指定父节点的需求,可按以下步骤修改代码:
1. 完善SceneNode的节点查找与插入逻辑
先给childNodes初始化避免空指针,添加递归查找父节点并插入子节点的方法,同时限制子节点数量不超过5:
public class SceneNode { @Id private String storyId; private String parentId; private String option; private String description; // 初始化子节点列表,防止空指针异常 private List<SceneNode> childNodes = new ArrayList<>(); // 修正构造方法的拼写错误(descrtion -> description) public SceneNode(String parentId, String option, String description) { this.parentId = parentId; this.option = option; this.description = description; } // 递归查找目标父节点并添加子节点 public boolean addChild(String targetParentId, SceneNode newNode) { // 当前节点是目标父节点时,添加子节点 if (this.storyId.equals(targetParentId)) { if (childNodes.size() >= 5) { throw new IllegalArgumentException("单个场景最多支持5个子节点"); } childNodes.add(newNode); return true; } // 遍历子节点继续查找 for (SceneNode child : childNodes) { if (child.addChild(targetParentId, newNode)) { return true; } } return false; } // getters & setters 省略 }
2. 改造StoryTree的insert方法
处理根节点创建逻辑,调用递归方法完成子节点插入:
public class StoryTree { private SceneNode root; public void insert(String parentId, String option, String description) { SceneNode newScene = new SceneNode(parentId, option, description); if (root == null) { // 根节点的parentId必须为空或null,作为故事起点 if (parentId == null || parentId.isEmpty()) { root = newScene; } else { throw new IllegalArgumentException("根节点的parentId不能指定"); } } else { boolean inserted = root.addChild(parentId, newScene); if (!inserted) { throw new IllegalArgumentException("未找到ID为" + parentId + "的父节点"); } } } // getters & setters 省略 }
3. 适配MongoDB持久化
因为使用MongoDB,可根据故事体量选择两种存储方式:
方式一:嵌入式存储(子节点嵌入父节点文档)
适合小体量故事树,查询时可一次性获取整棵树。保存父节点时需同步更新子节点列表:
@Repository public interface SceneNodeRepository extends MongoRepository<SceneNode, String> { } @Service public class StoryService { @Autowired private SceneNodeRepository repo; public SceneNode insertScene(String parentId, String option, String description) { SceneNode newNode = new SceneNode(parentId, option, description); if (parentId == null || parentId.isEmpty()) { // 保存根节点 return repo.save(newNode); } // 查找父节点并添加子节点 SceneNode parent = repo.findById(parentId) .orElseThrow(() -> new IllegalArgumentException("父节点不存在")); parent.addChild(parentId, newNode); // 先保存子节点生成ID,再更新父节点 newNode = repo.save(newNode); repo.save(parent); return newNode; } }
方式二:引用式存储(子节点仅存父ID)
适合大型故事树,避免单文档过大。插入时只需保存子节点的parentId:
@Repository public interface SceneNodeRepository extends MongoRepository<SceneNode, String> { List<SceneNode> findByParentId(String parentId); } @Service public class StoryService { @Autowired private SceneNodeRepository repo; public SceneNode insertScene(String parentId, String option, String description) { if (parentId != null && !parentId.isEmpty()) { // 校验父节点存在 repo.findById(parentId) .orElseThrow(() -> new IllegalArgumentException("父节点不存在")); } SceneNode newNode = new SceneNode(parentId, option, description); return repo.save(newNode); } // 查询指定父节点的所有子节点 public List<SceneNode> getChildren(String parentId) { return repo.findByParentId(parentId); } }
核心注意事项
- 确保
storyId唯一:MongoDB的@Id注解会自动生成唯一的ObjectId,无需手动维护 - 严格控制子节点数量:在
addChild方法中添加校验,符合业务要求 - 异常处理:针对父节点不存在、子节点超限等场景抛出明确异常,便于上层捕获处理
内容的提问来源于stack exchange,提问作者Dr.M
相关产品推荐
相关产品推荐

