waysun一路阳光

不轻易服输,不轻言放弃.--心是梦的舞台,心有多大,舞台有多大。踏踏实实做事,认认真真做人。

  BlogJava :: 首页 :: 新随笔 :: 联系 ::  :: 管理 ::
  167 随笔 :: 1 文章 :: 64 评论 :: 0 Trackbacks

http://www.javaeye.com/topic/602979

一篇不错的文章,收藏了,呵呵!

最近看到一个有意思的树形结构,为每个节点添加了lftrgt两个属性。这样查找该节点的子节点、查找该节点所有父节点,就不用去递归查询,只需要用betweenand语句就可以实现。下面以创建一个栏目树为例,以下是我的理解。

  一般来讲,我们创建栏目树的时候,大多只需要一个外键parentid来区分该节点属于哪个父节点。数据库的设计如下图:
这样一来,

1.查找该节点的所有子节点,则需要采用sql的递归语句:

 

Sql代码 复制代码
  1. select * from tableName connect by prior id=sj_parent_id start with  id=1  

 

 (oracle 写法,mysql目前不支持,如果mysql想查找树形,可以利用存储过程).

2.查找该节点的父节点的sql递归语句:

 

Sql代码 复制代码
  1. select * from tableName connect by prior sj_parent_id =id start with  id=1  

 

 如果数据量过大或者层次太多,那么这样操作是会影响性能的。

 

  “任何树形结构都可以用二叉树来表示”。其实我们创建的栏目树就是一个简型的二叉树。根据数据结构里面二叉树的遍历,再稍微修改下,将数据库设计如下图所示:

 


这样我们查找该节点的所有子节点,则只需要查找idlftrgt之间的所有节点即可。

1.查找该节点的所有子节点的Sql语句为:

<!--EndFragment-->

 

 

<!--EndFragment-->

Sql代码 复制代码
  1. select * from tb_subject s,tb_subject t where s.lft between t.lft and t.rgt and t.id=1  

 

2.查找该节点的所有父节点的sql语句为:

<!--EndFragment-->

Sql代码 复制代码
  1. select s.* from tb_subject s,tb_subject t where s.lft<t.lft and (s.rgt-s.lft)>1 and s.rgt>t.rgt and t.id=1  

 

 下面来详细讲解下,怎么用java来实现这种算法。

<!--EndFragment-->

 1. 新增节点

 新增节点比较简单,基本步骤为

 A. 查找当前插入节点的父节点的lft

 B. 将树形中所有lftrgt节点大于父节点左值的节点都+2

 C. 将父节点左值+1,左值+2分别作为当前节点的lftrgt

 因为项目中采用的是struts2+hibernate3.2+spring2.5的框架,代码如下:

<!--EndFragment-->

Java代码 复制代码
  1. public boolean onSave(Object entity, Serializable id, Object[] state,   
  2.             String[] propertyNames, Type[] types) {   
  3.         if (entity instanceof HibernateTree) {   
  4.             HibernateTree tree = (HibernateTree) entity;   
  5.             Long parentId = tree.getParentId();   
  6.             String beanName = tree.getClass().getName();   
  7.             Session session = getSession();   
  8.             FlushMode model = session.getFlushMode();   
  9.             session.setFlushMode(FlushMode.MANUAL);   
  10.             Integer myPosition = new Integer(0);   
  11.             //查找父节点的左值   
  12.             if (parentId != null) {   
  13.                 String hql = "select b.lft from " + beanName   
  14.                         + " b where b.id=:pid";   
  15.                 myPosition = (Integer) session.createQuery(hql).setLong("pid",   
  16.                         parentId).uniqueResult();   
  17.             }   
  18.             //将树形结构中所有大于父节点左值的右节点+2   
  19.             String hql1 = "update " + beanName   
  20.                     + " b set b.rgt = b.rgt + 2 WHERE b.rgt > :myPosition";   
  21.             //将树形结构中所有大于父节点左值的左节点+2   
  22.             String hql2 = "update " + beanName   
  23.                     + " b set b.lft = b.lft + 2 WHERE b.lft > :myPosition";   
  24.             if (!StringUtils.isBlank(tree.getTreeCondition())) {   
  25.                 hql1 += " and (" + tree.getTreeCondition() + ")";   
  26.                 hql2 += " and (" + tree.getTreeCondition() + ")";   
  27.             }   
  28.             session.createQuery(hql1).setInteger("myPosition", myPosition)   
  29.                     .executeUpdate();   
  30.             session.createQuery(hql2).setInteger("myPosition", myPosition)   
  31.                     .executeUpdate();   
  32.             session.setFlushMode(model);   
  33.             //定位自己的左值(父节点左值+1)和右值(父节点左值+2)   
  34.             for (int i = 0; i < propertyNames.length; i++) {   
  35.                 if (propertyNames[i].equals(HibernateTree.LFT)) {   
  36.                     state[i] = myPosition + 1;   
  37.                 }   
  38.                 if (propertyNames[i].equals(HibernateTree.RGT)) {   
  39.                     state[i] = myPosition + 2;   
  40.                 }   
  41.   
  42.             }   
  43.             return true;   
  44.         }   
  45.         return false;   
  46.     }  

 

 2. 修改节点

  修改的时候比较麻烦,具体步骤为:

  在修改lftrgt之前,当前节点的父节点id已经改变

a. 查出当前节点的左右节点(nodelftnodergt),并nodergt-nodelft+1 = span,获取父节点的左节点parentlft

b. 将所有大于parentlftlft(左节点)rgt(右节点)的值+span

c. 查找当前节点的左右节点(nodelftnodergt),并parentlft-nodelft+1 = offset

d. 将所有lft(左节点) between nodelft and nodergt的值+offset

e. 将所有大于nodergtlft(左节点)rgt(右节点)的值-span

 Java代码如下:

<!--EndFragment-->

Java代码 复制代码
  1. public void updateParent(HibernateTree tree, HibernateTree preParent,   
  2.             HibernateTree curParent) {   
  3.         if (preParent != null && preParent != null  
  4.                 && !preParent.equals(curParent)) {   
  5.             String beanName = tree.getClass().getName();   
  6.             // 获得节点位置   
  7.             String hql = "select b.lft,b.rgt from " + beanName   
  8.                     + " b where b.id=:id";   
  9.             Object[] position = (Object[]) super.createQuery(hql).setLong(   
  10.                     "id", tree.getId()).uniqueResult();   
  11.             System.out.println(hql+"| id = "+tree.getId());    
  12.             int nodeLft = ((Number) position[0]).intValue();   
  13.             int nodeRgt = ((Number) position[1]).intValue();   
  14.             int span = nodeRgt - nodeLft + 1;   
  15.             // 获得当前父节点左位置   
  16.             hql = "select b.lft from " + beanName + " b where b.id=:id";   
  17.             int parentLft = ((Number) super.createQuery(hql).setLong("id",   
  18.                     curParent.getId()).uniqueResult()).intValue();   
  19.   
  20.             System.out.println(hql+"| id = "+curParent.getId());   
  21.             // 先空出位置   
  22.             String hql1 = "update " + beanName + " b set b.rgt = b.rgt + "  
  23.                     + span + " WHERE b.rgt > :parentLft";   
  24.             String hql2 = "update " + beanName + " b set b.lft = b.lft + "  
  25.                     + span + " WHERE b.lft > :parentLft";   
  26.             if (!StringUtils.isBlank(tree.getTreeCondition())) {   
  27.                 hql1 += " and (" + tree.getTreeCondition() + ")";   
  28.                 hql2 += " and (" + tree.getTreeCondition() + ")";   
  29.             }   
  30.             super.createQuery(hql1).setInteger("parentLft", parentLft)   
  31.                     .executeUpdate();   
  32.             super.createQuery(hql2).setInteger("parentLft", parentLft)   
  33.                     .executeUpdate();   
  34.   
  35.             System.out.println(hql1+"| parentLft = "+parentLft);   
  36.             System.out.println(hql2+"| parentLft = "+parentLft);   
  37.                
  38.             // 再调整自己   
  39.             hql = "select b.lft,b.rgt from " + beanName + " b where b.id=:id";   
  40.             position = (Object[]) super.createQuery(hql).setLong("id",   
  41.                     tree.getId()).uniqueResult();   
  42.             System.out.println(hql+"| id = "+tree.getId());   
  43.             nodeLft = ((Number) position[0]).intValue();   
  44.             nodeRgt = ((Number) position[1]).intValue();   
  45.             int offset = parentLft - nodeLft + 1;   
  46.             hql = "update "  
  47.                     + beanName   
  48.                     + " b set b.lft=b.lft+:offset, b.rgt=b.rgt+:offset WHERE b.lft between :nodeLft and :nodeRgt";   
  49.             if (!StringUtils.isBlank(tree.getTreeCondition())) {   
  50.                 hql += " and (" + tree.getTreeCondition() + ")";   
  51.             }   
  52.             super.createQuery(hql).setParameter("offset", offset)   
  53.                     .setParameter("nodeLft", nodeLft).setParameter("nodeRgt",   
  54.                             nodeRgt).executeUpdate();   
  55.             System.out.println(hql+"| offset = "+offset+" | nodelft = "+nodeLft+" | nodergt = "+ nodeRgt);   
  56.             // 最后删除(清空位置)   
  57.             hql1 = "update " + beanName + " b set b.rgt = b.rgt - " + span   
  58.                     + " WHERE b.rgt > :nodeRgt";   
  59.             hql2 = "update " + beanName + " b set b.lft = b.lft - " + span   
  60.                     + " WHERE b.lft > :nodeRgt";   
  61.             if (tree.getTreeCondition() != null) {   
  62.                 hql1 += " and (" + tree.getTreeCondition() + ")";   
  63.                 hql2 += " and (" + tree.getTreeCondition() + ")";   
  64.             }   
  65.             super.createQuery(hql1).setParameter("nodeRgt", nodeRgt)   
  66.                     .executeUpdate();   
  67.             super.createQuery(hql2).setParameter("nodeRgt", nodeRgt)   
  68.                     .executeUpdate();   
  69.             System.out.println(hql1+"| nodeRgt = "+nodeRgt);   
  70.             System.out.println(hql2+"| nodeRgt = "+nodeRgt);   
  71.                
  72.         }   
  73.     }  

 

 3. 删除节点

 删除节点也比较简单,具体步骤为:

 A. 查找要删除节点的lft

 B. 将所有lftrgt大于删除节点lft值的都-2

 Java代码如下:

<!--EndFragment-->

 

 

 

 

<!--EndFragment-->
Java代码 复制代码
  1. public void onDelete(Object entity, Serializable id, Object[] state,   
  2.             String[] propertyNames, Type[] types) {   
  3.         if (entity instanceof HibernateTree) {   
  4.             HibernateTree tree = (HibernateTree) entity;   
  5.             String beanName = tree.getClass().getName();   
  6.             Session session = getSession();   
  7.             FlushMode model = session.getFlushMode();   
  8.             session.setFlushMode(FlushMode.MANUAL);   
  9.         //查找要删除的节点的左值   
  10.             String hql = "select b.lft from " + beanName + " b where b.id=:id";   
  11.             Integer myPosition = (Integer) session.createQuery(hql).setLong(   
  12.                     "id", tree.getId()).uniqueResult();   
  13. //将所有大于删除节点左值的rgt都-2   
  14.             String hql1 = "update " + beanName   
  15.                     + " b set b.rgt = b.rgt - 2 WHERE b.rgt > :myPosition";   
  16. //将所有大于删除节点左值的lft都-2   
  17.             String hql2 = "update " + beanName   
  18.                     + " b set b.lft = b.lft - 2 WHERE b.lft > :myPosition";   
  19.             if (tree.getTreeCondition() != null) {   
  20.                 hql1 += " and (" + tree.getTreeCondition() + ")";   
  21.                 hql2 += " and (" + tree.getTreeCondition() + ")";   
  22.             }   
  23.             session.createQuery(hql1).setInteger("myPosition", myPosition)   
  24.                     .executeUpdate();   
  25.             session.createQuery(hql2).setInteger("myPosition", myPosition)   
  26.                     .executeUpdate();   
  27.             session.setFlushMode(model);   
  28.         }   
  29.     }  

树形结构_算法.rar (37 KB)

posted on 2010-03-01 09:07 weesun一米阳光 阅读(4691) 评论(0)  编辑  收藏

只有注册用户登录后才能发表评论。


网站导航: