http://www.javaeye.com/topic/602979
一篇不錯(cuò)的文章,收藏了,呵呵!
最近看到一個(gè)有意思的樹(shù)形結(jié)構(gòu),為每個(gè)節(jié)點(diǎn)添加了lft和rgt兩個(gè)屬性。這樣查找該節(jié)點(diǎn)的子節(jié)點(diǎn)、查找該節(jié)點(diǎn)所有父節(jié)點(diǎn),就不用去遞歸查詢,只需要用between、and語(yǔ)句就可以實(shí)現(xiàn)。下面以創(chuàng)建一個(gè)欄目樹(shù)為例,以下是我的理解。
一般來(lái)講,我們創(chuàng)建欄目樹(shù)的時(shí)候,大多只需要一個(gè)外鍵parentid來(lái)區(qū)分該節(jié)點(diǎn)屬于哪個(gè)父節(jié)點(diǎn)。數(shù)據(jù)庫(kù)的設(shè)計(jì)如下圖:
這樣一來(lái),
1.查找該節(jié)點(diǎn)的所有子節(jié)點(diǎn),則需要采用sql的遞歸語(yǔ)句:
- select * from tableName connect by prior id=sj_parent_id start with id=1
select * from tableName connect by prior id=sj_parent_id start with id=1
(oracle 寫(xiě)法,mysql目前不支持,如果mysql想查找樹(shù)形,可以利用存儲(chǔ)過(guò)程).
2.查找該節(jié)點(diǎn)的父節(jié)點(diǎn)的sql遞歸語(yǔ)句:
- select * from tableName connect by prior sj_parent_id =id start with id=1
select * from tableName connect by prior sj_parent_id =id start with id=1
如果數(shù)據(jù)量過(guò)大或者層次太多,那么這樣操作是會(huì)影響性能的。
“任何樹(shù)形結(jié)構(gòu)都可以用二叉樹(shù)來(lái)表示”。其實(shí)我們創(chuàng)建的欄目樹(shù)就是一個(gè)簡(jiǎn)型的二叉樹(shù)。根據(jù)數(shù)據(jù)結(jié)構(gòu)里面二叉樹(shù)的遍歷,再稍微修改下,將數(shù)據(jù)庫(kù)設(shè)計(jì)如下圖所示:
這樣我們查找該節(jié)點(diǎn)的所有子節(jié)點(diǎn),則只需要查找id在lft和rgt之間的所有節(jié)點(diǎn)即可。
1.查找該節(jié)點(diǎn)的所有子節(jié)點(diǎn)的Sql語(yǔ)句為:
<!--EndFragment-->
<!--EndFragment-->
- select * from tb_subject s,tb_subject t where s.lft between t.lft and t.rgt and t.id=1
select * from tb_subject s,tb_subject t where s.lft between t.lft and t.rgt and t.id=1
2.查找該節(jié)點(diǎn)的所有父節(jié)點(diǎn)的sql語(yǔ)句為:
<!--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 t.id=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
下面來(lái)詳細(xì)講解下,怎么用java來(lái)實(shí)現(xiàn)這種算法。
<!--EndFragment-->
1. 新增節(jié)點(diǎn)
新增節(jié)點(diǎn)比較簡(jiǎn)單,基本步驟為
A. 查找當(dāng)前插入節(jié)點(diǎn)的父節(jié)點(diǎn)的lft值
B. 將樹(shù)形中所有lft和rgt節(jié)點(diǎn)大于父節(jié)點(diǎn)左值的節(jié)點(diǎn)都+2
C. 將父節(jié)點(diǎn)左值+1,左值+2分別作為當(dāng)前節(jié)點(diǎn)的lft和rgt
因?yàn)轫?xiàng)目中采用的是struts2+hibernate3.2+spring2.5的框架,代碼如下:
<!--EndFragment-->
- 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();
- }
-
- String hql1 = "update " + beanName
- + " b set b.rgt = b.rgt + 2 WHERE b.rgt > :myPosition";
-
- 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);
-
- 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;
- }
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);
//查找父節(jié)點(diǎn)的左值
if (parentId != null) {
String hql = "select b.lft from " + beanName
+ " b where b.id=:pid";
myPosition = (Integer) session.createQuery(hql).setLong("pid",
parentId).uniqueResult();
}
//將樹(shù)形結(jié)構(gòu)中所有大于父節(jié)點(diǎn)左值的右節(jié)點(diǎn)+2
String hql1 = "update " + beanName
+ " b set b.rgt = b.rgt + 2 WHERE b.rgt > :myPosition";
//將樹(shù)形結(jié)構(gòu)中所有大于父節(jié)點(diǎn)左值的左節(jié)點(diǎn)+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);
//定位自己的左值(父節(jié)點(diǎn)左值+1)和右值(父節(jié)點(diǎn)左值+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. 修改節(jié)點(diǎn)
修改的時(shí)候比較麻煩,具體步驟為:
在修改lft和rgt之前,當(dāng)前節(jié)點(diǎn)的父節(jié)點(diǎn)id已經(jīng)改變
a. 查出當(dāng)前節(jié)點(diǎn)的左右節(jié)點(diǎn)(nodelft、nodergt),并nodergt-nodelft+1 = span,獲取父節(jié)點(diǎn)的左節(jié)點(diǎn)parentlft
b. 將所有大于parentlft的lft(左節(jié)點(diǎn))、rgt(右節(jié)點(diǎn))的值+span
c. 查找當(dāng)前節(jié)點(diǎn)的左右節(jié)點(diǎn)(nodelft、nodergt),并parentlft-nodelft+1 = offset
d. 將所有lft(左節(jié)點(diǎn)) between nodelft and nodergt的值+offset
e. 將所有大于nodergt的lft(左節(jié)點(diǎn))、rgt(右節(jié)點(diǎn))的值-span
Java代碼如下:
<!--EndFragment-->
- 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);
-
- }
- }
public void updateParent(HibernateTree tree, HibernateTree preParent,
HibernateTree curParent) {
if (preParent != null && preParent != null
&& !preParent.equals(curParent)) {
String beanName = tree.getClass().getName();
// 獲得節(jié)點(diǎn)位置
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;
// 獲得當(dāng)前父節(jié)點(diǎn)左位置
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);
// 再調(diào)整自己
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. 刪除節(jié)點(diǎn)
刪除節(jié)點(diǎn)也比較簡(jiǎn)單,具體步驟為:
A. 查找要?jiǎng)h除節(jié)點(diǎn)的lft值
B. 將所有lft和rgt大于刪除節(jié)點(diǎn)lft值的都-2
Java代碼如下:
<!--EndFragment-->
<!--EndFragment-->
- 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();
-
- String hql1 = "update " + beanName
- + " b set b.rgt = b.rgt - 2 WHERE b.rgt > :myPosition";
-
- 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);
- }
- }
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);
//查找要?jiǎng)h除的節(jié)點(diǎn)的左值
String hql = "select b.lft from " + beanName + " b where b.id=:id";
Integer myPosition = (Integer) session.createQuery(hql).setLong(
"id", tree.getId()).uniqueResult();
//將所有大于刪除節(jié)點(diǎn)左值的rgt都-2
String hql1 = "update " + beanName
+ " b set b.rgt = b.rgt - 2 WHERE b.rgt > :myPosition";
//將所有大于刪除節(jié)點(diǎn)左值的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);
}
}
樹(shù)形結(jié)構(gòu)_算法.rar (37 KB)