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

常见的层级结构如下:

全国总行

-- 北京分行

-- 上海分行

-- 福建分行

-- 福州分行

-- 厦门分行

-- 思明区分行

-- 财政部

-- 开发部

-- 行政部

-- 张三

-- 李四

-- 广东分行

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

毗邻目录算法:

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

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

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;
}

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

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

  1. PCM混音算法 C++实现 (包括归一化加权算法,时间片切割算法,幅值简单叠加算法)

    文章目录 概述 输入 输出 混音算法 注意 代码 数据类型 头文件 源文件 概述 参考: 归一化.叠加+均值 相加相乘的混音算法 实现了PCM混音算法.以5个wav混音,其中每个wav长度均为2205 ...

  2. 相似图像搜索的哈希算法思想及实现(差值哈希算法和均值哈希算法)

    图像相似度比较哈希算法: 什么是哈希(Hash)? • 散列函数(或散列算法,又称哈希函数,英语:Hash Function)是一种从任何一种数据中创建小 的数字"指纹"的方法.散 ...

  3. 左右值无限分类实现算法

    转:左右值无限分类实现算法 左右值无限分类实现算法 一.引言 产品分类,多级的树状结构的论坛,邮件列表等许多地方我们都会遇到这样的问题:如何存储多级结构的数据?在PHP的应用中,提供后台数据存储的通常 ...

  4. php 无限子站cms,PHP整理CMS无限层级目录(毗邻目录模式)

    CMS中的分类往往是可以无限划分子目录的,存储数据的过程比较容易实现,但是取目录层级的方式却比较难搞. 常用的目录划分模式有毗邻目录模式和预排序遍历树(左右值无限分类法).这里只讨论前一种模式的目录获 ...

  5. MySQL 所推荐的左右值法(毗邻目录法、预排序历遍法)

    毗邻目录法: 这种方法说白了就是子类,依赖父类,父类依赖爷爷类,爷爷类可以有多个儿子类,跟父类平级的类.一层一层的. 预排序历遍法: 这种算法比较高端,使用的是mysql官方推荐的左右算法. 使用场合 ...

  6. 基于并行思想的简单算法优化(最值,排序)

    并行算法(Parallel Algorithm)是计算机科学中一门单独的分支,由于本人水平有限,时间有限,只大致了解了并行算法的基本模型以及几个常见算法的优化改进,下面以(1)求序列最大值 (2)归并 ...

  7. 目录树算法(毗邻目录模式、预排序遍历树算法)

    1 毗邻目录模式(adjacency list model) 2 预排序遍历树算法(modified preorder tree traversal algorithm) 常用的是第一种:毗邻目录模式 ...

  8. 策略迭代算法和值函数迭代算法

    策略迭代算法和值函数迭代算法 文章目录 1. 回顾与引言 2. 思路介绍 3. 策略评估算法 3. 策略优化算法 4. 策略迭代算法和值函数迭代算法 5. 代码实现 6. 强化学习与最优控制 1. 回 ...

  9. 一类能产生无限种可能的随机算法--无限随机算法

    文章目录 前言 底层实现原理 python实现 java实现 无限随机算法的本质 最后 前言 最近发现一类算法,我称它们为无限随机算法,它们的特点是产生一个输出,这个输出有无限种可能.例如下面这个算法 ...

最新文章

  1. element UI el-autocomplete 带输入建议的输入框
  2. jenkins slave在master显示和运行问题
  3. php系统函数代码,PHP自定义函数+系统函数库(代码示例)
  4. VS2008下最新X264(svn 2009.0216)编译不过的解决办法(附编译通过+修改内存泄露版本)
  5. 作为一个算法攻城狮,你训练的算法翻车了该怎么办?
  6. Java属性中指定Json的属性名称(序列化和反序列化)
  7. Java中使用队列Queue
  8. 理解 JavaScript 闭包{转载}
  9. 配色方案没有头绪?看看给你灵感的专业指导
  10. Ubuntu Linux环境下的Android开发环境的配置
  11. ae 的动画导出为html,Bodymovin v5.5.3 – AE导出Web动画插件+使用教程
  12. 九章云极DataCanvas公司荣获机器之心三大奖项,助力产业数智化升级
  13. 苹果手机备忘录html转pdf,苹果手机自带的扫描仪,一键便能转成PDF文件,你不会才知道吧...
  14. My Java 总结
  15. 【APICloud系列|34】上架华为应用市场缺少免责函?
  16. Android客户端登录注册模块
  17. 发动机连杆产品配置管理PDM解决方案
  18. 【拜读】HTTPS和SSL/TLS 协议
  19. 常用的Linux快捷键 [译]
  20. HTTP各版本号的区别

热门文章

  1. 美国旅游签证归来(上海领馆)
  2. 【STM32】使用STM32cubeMX的库读写FLASH数据
  3. 网络编程(3)-----------Javaweb
  4. mysql 廖雪峰_关系模型 - 索引 - 《廖雪峰 SQL教程(MySQL)》 - IT Book
  5. 地图省界线什么样_echarts geo 下的regions 单独修改地图省份界线样式与颜色
  6. 亲身经历灵魂附体与出马仙之说
  7. antd table分页,关于react的antd表格分页的问题
  8. java 出路 xls_java生成xls
  9. 用 Matlab 实现 GS 算法设计计算全息图
  10. 仿百度联想词下拉列表,键盘上下键选值