毗邻目录算法与左右值无限分支算法的结合

之前在开发过程当中会涉及到一些层级结构访问的问题。这种层级结构往往是无限的,而且十分常见。如企业内部的员工分组。

常见的层级结构如下:

全国总行

      -- 北京分行

      -- 上海分行

      -- 福建分行

          -- 福州分行

          -- 厦门分行

              -- 思明区分行

                  -- 财政部

                  -- 开发部

                  -- 行政部

                      -- 张三

                      -- 李四

      -- 广东分行


向上图的访问结构是非常常见的,而传统的算法会采用一下两种方式进行访问


毗邻目录算法:

即通过标识上一级目录来索引上一级,

传统的数据库结构会用如下的设计方式

id    parentId   name  isLeaf  

通过parentId既可以找到上级的目录。

而通过这种方式有一个最大的问题就是,当要遍历某个叶子节点的层级关系时,需要不断递归到根节点为止,这种算法只适合更新比较频繁,层级查询比较少的场景,所以就有了一下的算法


左右值无限分支算法:

通过增加左右值来标志某个层级

数据库会如下所示:

id   name  lft   rgt  其中lft代表左值,rgt代表右值,每个节点的父节点的左右值将会包含子节点的左右值

如下:                                

                                                                                    1    全国总行 24

                                                                                               |

                     ________________________________________________________________

                    |                                       |                                            |                                                                |

           2 北京分行 3             4 上海分行 5                         6 福建分行 21                                         22  广东分行 23

                                                                                                         |

                                                           __________________________________

                                                         |                                                                                 |

                                                7  福州分行   8                                                         9   厦门分行 20

                                                                                                                                          |

                                                                                             ———————————————————————

                                                                                            |                                            |                                              |

                                                                                 10      财政部       11          12   开发部  13                         14 行政部 19

                                                                                                                                                                                        |

                                                                                                                                                                        —————————

                                                                                                                                                                       |                                    |

                                                                                                                                                              15   张三  16                  17 李四 18


使用左右值方式可以很好的表示层级方式,如要获取李四的整个层级只要找到所有 左值小于17且右值大于18的节点即可

但是左右值算法的缺点在于更新的复杂,即每当子节点更新(添加,删除,移动)都会影响到很多的节点,所以这种算法使用于更新比较少,查询比较频繁的场景


所以综合实际的场景,需要把以上两种算法结合起来,对于部门等层级关系,采用左右值算法保存,因为这部分关系相对来讲更新到频率比较低,利用左右值算法可以很快的查询到整个层级关系,而对于具体的人员,采用毗邻目录算法来保存上级部分的关系。因为人员的更新比较频繁,更新人员只要更新parentId即可。


下面附上一些更新部门等级关系的语句


新加

Session session = this.getSession();
try {
session.beginTransaction().begin();
//实时从数据库中获取记录,保持与数据库中一致
UserGroup parentGroup = this.findById(transientInstance.getUserGroup().getId());
if(parentGroup==null) return;
transientInstance.setUserGroup(parentGroup);
//更新所有父节点的右值
String updateSql = "update UserGroup as Model set Model.right = Model.right + 2 where Model.right >= :right";
Query query1 = session.createQuery(updateSql);
query1.setParameter("right", parentGroup.getRight());
query1.executeUpdate();

//更新所有父节点的左值
updateSql = "update UserGroup as Model set Model.left = Model.left + 2 where Model.left >= :right";
Query query2 = session.createQuery(updateSql);
query2.setParameter("right", parentGroup.getRight());
query2.executeUpdate();

//设置新添加节点的的左值和右值
transientInstance.setLeft(parentGroup.getRight());
transientInstance.setRight(parentGroup.getRight() + 1);
transientInstance.setTempFlag(0);

session.save(transientInstance);

session.beginTransaction().commit();
session.close();
log.debug("save successful");
} catch (RuntimeException re) {
session.beginTransaction().rollback();
session.close();
log.error("save failed", re);
throw re;
}


删除


Session session = this.getSession();
try 
{
session.beginTransaction().begin();
//实时从数据库中获取记录,保持与数据库中一致
UserGroup newGroup = this.findById(persistentInstance.getId());
if(newGroup==null) return;
Integer left = newGroup.getLeft();
Integer right = newGroup.getRight();

session.delete(persistentInstance);

//更新所有父节点的右值
String updateSql = "update UserGroup as Model set Model.right = Model.right - (:right - :left + 1) where Model.right > :right";
Query query1 = session.createQuery(updateSql);
query1.setParameter("right", right);
query1.setParameter("left", left);
query1.executeUpdate();

//更新所有父节点的左值
updateSql = "update UserGroup as Model set Model.left = Model.left - (:right - :left + 1) where Model.left >= :left";
Query query2 = session.createQuery(updateSql);
query2.setParameter("right", right);
query2.setParameter("left", left);
query2.executeUpdate();

session.beginTransaction().commit();
session.close();
log.debug("delete successful");
} catch (RuntimeException re) {
session.beginTransaction().rollback();
session.close();
log.error("delete failed", re);
throw re;
}


移动


try
{
session.beginTransaction().begin();

Integer left = instance.getLeft();
Integer right = instance.getRight();

//为移动节点及所有子节点的设置一个标记
String updateSql = "update UserGroup as Model set Model.tempFlag = 1 where Model.left >= :left and Model.right <= :right";
Query query1 = session.createQuery(updateSql);
query1.setParameter("right", right);
query1.setParameter("left", left);
query1.executeUpdate();

//【按删除该节点的方式更新左右值,除删除节点和子节点除外】
//更新所有父节点的右值
updateSql = "updateUserGroup as Model set Model.right = Model.right - (:right - :left + 1) where Model.right > :right and Model.tempFlag != 1";
Query query2 = session.createQuery(updateSql);
query2.setParameter("right", right);
query2.setParameter("left", left);
query2.executeUpdate();

//更新所有父节点的左值
updateSql = "update UserGroup as Model set Model.left = Model.left - (:right - :left + 1) where Model.left >= :left and Model.tempFlag != 1";
Query query3 = session.createQuery(updateSql);
query3.setParameter("right", right);
query3.setParameter("left", left);
query3.executeUpdate();

//重新获取目标父亲节点的左右值
String querySql = "from UserGroup as Model where Model.id = :groupId";
Query query4 = session.createQuery(querySql);
query4.setParameter("groupId", instance.getUserGroup().getId());
List listTemp = query4.list();
if(listTemp.size() == 0 || listTemp.size() > 1)
{
throw new RuntimeException("target group with id :" + instance.getUserGroup().getId() + " is not exist in database.");
}
UserGroup targetGroup = (UserGroup) listTemp.get(0);
Integer targetGroupRight = targetGroup.getRight();


Integer targetGroupLeft = targetGroup.getLeft();

//【按增加节点的方式更新左右值,除删除节点和子节点除外】
//更新所有父节点的右值
updateSql = "update UserGroup as Model set Model.right = Model.right + (:right - :left + 1) where Model.right >= :targetRight and Model.tempFlag != 1";
Query query5 = session.createQuery(updateSql);
query5.setParameter("left", left);
query5.setParameter("right", right);
query5.setParameter("targetRight", targetGroupRight);
query5.executeUpdate();

//更新所有父节点的左值
updateSql = "update UserGroup  as Model set Model.left = Model.left + (:right - :left + 1) where Model.left >= :targetRight and Model.tempFlag != 1";
Query query6 = session.createQuery(updateSql);
query6.setParameter("left", left);
query6.setParameter("right", right);
query6.setParameter("targetRight", targetGroupRight);
query6.executeUpdate();


//【为移动节点及所有子节点设置左右值及清除临时标记】
updateSql = "update UserGroup  as Model set Model.left = Model.left + :offset, Model.right = Model.right + :offset, Model.tempFlag = 0 where Model.left >= :left and Model.right <= :right and Model.tempFlag = 1";
Query query7 = session.createQuery(updateSql);
query7.setParameter("right", right);
query7.setParameter("left", left);


Integer offset = targetGroupRight - left;
query7.setParameter("offset", offset);
query7.executeUpdate();



instance.setLeft(instance.getLeft() + offset);
instance.setRight(instance.getRight() + offset);
session.saveOrUpdate(instance);

session.beginTransaction().commit();
session.close();
}
catch(RuntimeException re)
{
session.beginTransaction().rollback();
session.close();
throw re;
}



由于是第一次写博客,写的不好,清见谅。






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值