什么是嵌套集模型
详情可以看我在掘金上的这篇文章,juejin.cn/post/684490…
大致上来说,它就是一种层级结构的模型。当对层级结构有许多的遍历操作时,使用它会十分方便。比如获取某个节点到根节点的路径,统计节点的子节点数量,或者节点及其子节点所关联的实体数量(如获取产品分类树,及其每个分类节点所拥有的产品数量)。
不过它的缺点是增加、删除、移动节点会相对而言较为复杂以及消耗性能。 尤其是移动节点,更加复杂。然而在实际应用场景当中,改变某个分类的父分类是一个比较常见的需求。下面我们就来介绍该如何实现这个需求。
如何改变节点的父亲节点
我们以如下的分类树为例子:

如果我们想让游戏节点的父节点改成音乐节点,可以按照以下步骤:
- 给子树创建新的空间
UPDATE category SET lft = lft + :width WHERE lft >= :newpos
UPDATE category SET rgt = rgt + :width WHERE rgt >= :newpos
- 把子树移动到新空间
UPDATE tags SET lft = lft + :distance, rgt = rgt + :distance
WHERE lft >= :tmppos AND rgt < :tmppos + :width
- 去除被之前子树占据的空间
UPDATE category SET lft = lft - :width WHERE lft > :oldRgt
UPDATE category SET rgt = rgt - :width WHERE rgt > :oldRgt
各变量说明
int newpos = 4 + 1 // 新的父节点的lft值+1;在这里音乐节点的lft值是4
int width = node.getRgt() - node.getLft() + 1; // 原子树的宽度
int distance = newpos - node.getLft(); // 节点到新的位置的距离
int tmppos = node.getLft();
// 向后移动必须考虑新空间
if (distance < 0) {
distance -= width;
tmppos += width;
}
再经过上面的操作后,整个树将会变成如下:

总结
除了上面的5次sql操作,其实我们还需要查询被移动的节点以及新的父节点。所以总的来说至少需要6次sql查询。一般情况下,修改节点的父节点是一个不太频繁的操作,所以相对于它带来的好处,其实是可以接受这样的性能消耗的。