最近看到一个有意思的树形结构,为每个节点添加了lft 和 rgt 两个属性。这样查找该节点的子节点、 查找该节点所有父节点,就不用去递归查询,只需要用 between 、 and
parentid 来区分该节点属于哪个父节点。数据库的设计如下图:
这样一来,
1.查找该节点的所有子节点,则需要采用 sql
- select * from tableName connect by prior id=sj_parent_id start with
select * from tableName connect by prior id=sj_parent_id start with id=1
( oracle 写法, mysql 目前不支持,如果 mysql ).
2.查找该节点的父节点 的 sql
- select * from tableName connect by prior sj_parent_id =id start with
select * from tableName connect by prior sj_parent_id =id start with id=1
如果数据量过大或者层次太多,那么这样操作是会影响性能的。
“任何树形结构都可以用二叉树来表示”。其实我们创建的栏目树就是一个简型的二叉树。根据数据结构里面二叉树的遍历,再稍微修改下,将数据库设计如下图 所示:
这样我们查找该节点的所有子节点,则只需要查找id 在 lft 和 rgt
1.查找该节点的所有子节点的 S ql语句为:
<!--EndFragment-->
<!--EndFragment-->
- select * from tb_subject s,tb_subject t where s.lft between t.lft and t.rgt and
select * from tb_subject s,tb_subject t where s.lft between t.lft and t.rgt and t.id=1
2.查找该节点的所有父节点的 sql
<!--EndFragment-->
- 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
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. 将树形中所有lft 和 rgt 节点大于父节点左值的节点都 +2
C. 将父节点左值+1 ,左值 +2 分别作为当前节点的 lft 和 rgt
struts2+hibernate3.2+spring2.5
<!--EndFragment-->
1. public boolean
2. String[] propertyNames, Type[] types) {
3. if (entity instanceof
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. new Integer( 0
11. //查找父节点的左值
12. if (parentId != null
13. "select b.lft from "
14. " b where b.id=:pid"
15. "pid"
16. parentId).uniqueResult();
17. }
18. //将树形结构中所有大于父节点左值的右节点+2
19. "update "
20. " b set b.rgt = b.rgt + 2 WHERE b.rgt > :myPosition"
21. //将树形结构中所有大于父节点左值的左节点+2
22. "update "
23. " b set b.lft = b.lft + 2 WHERE b.lft > :myPosition"
24. if
25. " and (" + tree.getTreeCondition() + ")"
26. " and (" + tree.getTreeCondition() + ")"
27. }
28. "myPosition"
29. .executeUpdate();
30. "myPosition"
31. .executeUpdate();
32. session.setFlushMode(model);
33. //定位自己的左值(父节点左值+1)和右值(父节点左值+2)
34. for ( int i = 0
35. if
36. 1
37. }
38. if
39. 2
40. }
41.
42. }
43. return true
44. }
45. return false
46. }
public boolean onSave(Object entity, Serializable id, Object[] state,
String[] propertyNames, Type[] types) {
if (entity instanceof HibernateTree) {
HibernateTree tree = (HibernateTree) entity;
Long parentId = tree.getParentId();
String beanName = tree.getClass().getName();
Session session = getSession();
FlushMode model = session.getFlushMode();
session.setFlushMode(FlushMode.MANUAL);
Integer myPosition = new Integer(0);
//查找父节点的左值
if (parentId != null) {
String hql = "select b.lft from " + beanName
+ " b where b.id=:pid";
myPosition = (Integer) session.createQuery(hql).setLong("pid",
parentId).uniqueResult();
}
//将树形结构中所有大于父节点左值的右节点+2
String hql1 = "update " + beanName
+ " b set b.rgt = b.rgt + 2 WHERE b.rgt > :myPosition";
//将树形结构中所有大于父节点左值的左节点+2
String hql2 = "update " + beanName
+ " b set b.lft = b.lft + 2 WHERE b.lft > :myPosition";
if (!StringUtils.isBlank(tree.getTreeCondition())) {
hql1 += " and (" + tree.getTreeCondition() + ")";
hql2 += " and (" + tree.getTreeCondition() + ")";
}
session.createQuery(hql1).setInteger("myPosition", myPosition)
.executeUpdate();
session.createQuery(hql2).setInteger("myPosition", myPosition)
.executeUpdate();
session.setFlushMode(model);
//定位自己的左值(父节点左值+1)和右值(父节点左值+2)
for (int i = 0; i < propertyNames.length; i++) {
if (propertyNames[i].equals(HibernateTree.LFT)) {
state[i] = myPosition + 1;
}
if (propertyNames[i].equals(HibernateTree.RGT)) {
state[i] = myPosition + 2;
}
}
return true;
}
return false;
}
2. 修改节点
修改的时候比较麻烦,具体步骤为:
lft 和 rgt 之前,当前节点的父节点 id
a. 查出当前节点的左右节点(nodelft 、 nodergt ),并 nodergt-nodelft+1 = span ,获取父节点的左节点 parentlft
b. 将所有大于parentlft 的 lft( 左节点 ) 、 rgt( 右节点 ) 的值 +span
c. 查找当前节点的左右节点(nodelft 、 nodergt ),并 parentlft-nodelft+1 = offset
d. 将所有lft( 左节点 ) between nodelft and nodergt 的值 +offset
e. 将所有大于nodergt 的 lft( 左节点 ) 、 rgt( 右节点 ) 的值 -span
J ava代码如下:
<!--EndFragment-->
1. public void
2. HibernateTree curParent) {
3. if (preParent != null && preParent != null
4. && !preParent.equals(curParent)) {
5. String beanName = tree.getClass().getName();
6. // 获得节点位置
7. "select b.lft,b.rgt from "
8. " b where b.id=:id"
9. super
10. "id"
11. "| id = "
12. int nodeLft = ((Number) position[ 0
13. int nodeRgt = ((Number) position[ 1
14. int span = nodeRgt - nodeLft + 1
15. // 获得当前父节点左位置
16. "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. "| id = "
21. // 先空出位置
22. "update " + beanName + " b set b.rgt = b.rgt + "
23. " WHERE b.rgt > :parentLft"
24. "update " + beanName + " b set b.lft = b.lft + "
25. " WHERE b.lft > :parentLft"
26. if
27. " and (" + tree.getTreeCondition() + ")"
28. " and (" + tree.getTreeCondition() + ")"
29. }
30. super .createQuery(hql1).setInteger( "parentLft"
31. .executeUpdate();
32. super .createQuery(hql2).setInteger( "parentLft"
33. .executeUpdate();
34.
35. "| parentLft = "
36. "| parentLft = "
37.
38. // 再调整自己
39. "select b.lft,b.rgt from " + beanName + " b where b.id=:id"
40. super .createQuery(hql).setLong( "id"
41. tree.getId()).uniqueResult();
42. "| id = "
43. 0
44. 1
45. int offset = parentLft - nodeLft + 1
46. "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
50. " and (" + tree.getTreeCondition() + ")"
51. }
52. super .createQuery(hql).setParameter( "offset"
53. "nodeLft" , nodeLft).setParameter( "nodeRgt"
54. nodeRgt).executeUpdate();
55. "| offset = " +offset+ " | nodelft = " +nodeLft+ " | nodergt = "
56. // 最后删除(清空位置)
57. "update " + beanName + " b set b.rgt = b.rgt - "
58. " WHERE b.rgt > :nodeRgt"
59. "update " + beanName + " b set b.lft = b.lft - "
60. " WHERE b.lft > :nodeRgt"
61. if (tree.getTreeCondition() != null
62. " and (" + tree.getTreeCondition() + ")"
63. " and (" + tree.getTreeCondition() + ")"
64. }
65. super .createQuery(hql1).setParameter( "nodeRgt"
66. .executeUpdate();
67. super .createQuery(hql2).setParameter( "nodeRgt"
68. .executeUpdate();
69. "| nodeRgt = "
70. "| nodeRgt = "
71.
72. }
73. }
public void updateParent(HibernateTree tree, HibernateTree preParent,
HibernateTree curParent) {
if (preParent != null && preParent != null
&& !preParent.equals(curParent)) {
String beanName = tree.getClass().getName();
// 获得节点位置
String hql = "select b.lft,b.rgt from " + beanName
+ " b where b.id=:id";
Object[] position = (Object[]) super.createQuery(hql).setLong(
"id", tree.getId()).uniqueResult();
System.out.println(hql+"| id = "+tree.getId());
int nodeLft = ((Number) position[0]).intValue();
int nodeRgt = ((Number) position[1]).intValue();
int span = nodeRgt - nodeLft + 1;
// 获得当前父节点左位置
hql = "select b.lft from " + beanName + " b where b.id=:id";
int parentLft = ((Number) super.createQuery(hql).setLong("id",
curParent.getId()).uniqueResult()).intValue();
System.out.println(hql+"| id = "+curParent.getId());
// 先空出位置
String hql1 = "update " + beanName + " b set b.rgt = b.rgt + "
+ span + " WHERE b.rgt > :parentLft";
String hql2 = "update " + beanName + " b set b.lft = b.lft + "
+ span + " WHERE b.lft > :parentLft";
if (!StringUtils.isBlank(tree.getTreeCondition())) {
hql1 += " and (" + tree.getTreeCondition() + ")";
hql2 += " and (" + tree.getTreeCondition() + ")";
}
super.createQuery(hql1).setInteger("parentLft", parentLft)
.executeUpdate();
super.createQuery(hql2).setInteger("parentLft", parentLft)
.executeUpdate();
System.out.println(hql1+"| parentLft = "+parentLft);
System.out.println(hql2+"| parentLft = "+parentLft);
// 再调整自己
hql = "select b.lft,b.rgt from " + beanName + " b where b.id=:id";
position = (Object[]) super.createQuery(hql).setLong("id",
tree.getId()).uniqueResult();
System.out.println(hql+"| id = "+tree.getId());
nodeLft = ((Number) position[0]).intValue();
nodeRgt = ((Number) position[1]).intValue();
int offset = parentLft - nodeLft + 1;
hql = "update "
+ beanName
+ " b set b.lft=b.lft+:offset, b.rgt=b.rgt+:offset WHERE b.lft between :nodeLft and :nodeRgt";
if (!StringUtils.isBlank(tree.getTreeCondition())) {
hql += " and (" + tree.getTreeCondition() + ")";
}
super.createQuery(hql).setParameter("offset", offset)
.setParameter("nodeLft", nodeLft).setParameter("nodeRgt",
nodeRgt).executeUpdate();
System.out.println(hql+"| offset = "+offset+" | nodelft = "+nodeLft+" | nodergt = "+ nodeRgt);
// 最后删除(清空位置)
hql1 = "update " + beanName + " b set b.rgt = b.rgt - " + span
+ " WHERE b.rgt > :nodeRgt";
hql2 = "update " + beanName + " b set b.lft = b.lft - " + span
+ " WHERE b.lft > :nodeRgt";
if (tree.getTreeCondition() != null) {
hql1 += " and (" + tree.getTreeCondition() + ")";
hql2 += " and (" + tree.getTreeCondition() + ")";
}
super.createQuery(hql1).setParameter("nodeRgt", nodeRgt)
.executeUpdate();
super.createQuery(hql2).setParameter("nodeRgt", nodeRgt)
.executeUpdate();
System.out.println(hql1+"| nodeRgt = "+nodeRgt);
System.out.println(hql2+"| nodeRgt = "+nodeRgt);
}
}
3. 删除节点
删除节点也比较简单,具体步骤为:
A. 查找要删除节点的lft
B. 将所有lft 和 rgt 大于删除节点 lft 值的都 -2
J ava代码如下:
<!--EndFragment-->
<!--EndFragment-->
1. public void
2. String[] propertyNames, Type[] types) {
3. if (entity instanceof
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. "select b.lft from " + beanName + " b where b.id=:id"
11. Integer myPosition = (Integer) session.createQuery(hql).setLong(
12. "id"
13. //将所有大于删除节点左值的rgt都-2
14. "update "
15. " b set b.rgt = b.rgt - 2 WHERE b.rgt > :myPosition"
16. //将所有大于删除节点左值的lft都-2
17. "update "
18. " b set b.lft = b.lft - 2 WHERE b.lft > :myPosition"
19. if (tree.getTreeCondition() != null
20. " and (" + tree.getTreeCondition() + ")"
21. " and (" + tree.getTreeCondition() + ")"
22. }
23. "myPosition"
24. .executeUpdate();
25. "myPosition"
26. .executeUpdate();
27. session.setFlushMode(model);
28. }
29. }
public void onDelete(Object entity, Serializable id, Object[] state,
String[] propertyNames, Type[] types) {
if (entity instanceof HibernateTree) {
HibernateTree tree = (HibernateTree) entity;
String beanName = tree.getClass().getName();
Session session = getSession();
FlushMode model = session.getFlushMode();
session.setFlushMode(FlushMode.MANUAL);
//查找要删除的节点的左值
String hql = "select b.lft from " + beanName + " b where b.id=:id";
Integer myPosition = (Integer) session.createQuery(hql).setLong(
"id", tree.getId()).uniqueResult();
//将所有大于删除节点左值的rgt都-2
String hql1 = "update " + beanName
+ " b set b.rgt = b.rgt - 2 WHERE b.rgt > :myPosition";
//将所有大于删除节点左值的lft都-2
String hql2 = "update " + beanName
+ " b set b.lft = b.lft - 2 WHERE b.lft > :myPosition";
if (tree.getTreeCondition() != null) {
hql1 += " and (" + tree.getTreeCondition() + ")";
hql2 += " and (" + tree.getTreeCondition() + ")";
}
session.createQuery(hql1).setInteger("myPosition", myPosition)
.executeUpdate();
session.createQuery(hql2).setInteger("myPosition", myPosition)
.executeUpdate();
session.setFlushMode(model);
}
}
<!--EndFragment-->
<!--EndFragment-->
<!--EndFragment-->
<!--EndFragment-->
<!--EndFragment-->