适用场景:
在一个程序运行周期内,需要大量的对同一颗树状数据进行各种查询、运算
此管理器可缓存树状数据并做相应处理,减少数据库IO,极大提升效率
实现方法:
1.为树节点建立实体类,数据以树状结构组织
2.为每一个节点建立索引,使用HashMap实现索引功能
------------------------------------代码分割线------------------------------------
实体类:
/* 此类是一个树节点实体类 */
class TreeDataEntity
/* 自己的ID */
property string id;
/* 父节点Id */
property string parentId;
/* 父节点 */
property GT_COMMON:UTIL:entity:TreeDataEntity parent;
/* 存放节点信息 */
property any anyProperty;
/* 孩子节点数组 */
property array of GT_COMMON:UTIL:entity:TreeDataEntity childrenArray;
method TreeDataEntity(&_id As string, &_parentId As string);
method addChild(&childNode As GT_COMMON:UTIL:entity:TreeDataEntity);
method getAllChildren(&dataBearer As array of GT_COMMON:UTIL:entity:TreeDataEntity);
property integer treeLvl get set;
property integer childrenCount get;
private
instance integer &_treeLvl;
end-class;
method TreeDataEntity
/+ &_id as String, +/
/+ &_parentId as String +/
%This.childrenArray = CreateArrayRept(%This, 0);
%This.treeLvl = - 1;
%This.id = &_id;
%This.parentId = &_parentId;
end-method;
method addChild
/+ &childNode as GT_COMMON:UTIL:entity:TreeDataEntity +/
%This.childrenArray.Push(&childNode);
end-method;
method getAllChildren
/+ &dataBearer as Array of GT_COMMON:UTIL:entity:TreeDataEntity +/
Local integer &i;
For &i = 1 To &childrenArray.Len
&dataBearer.Push(&childrenArray [&i]);
&childrenArray [&i].getAllChildren(&dataBearer);
End-For;
end-method;
set treeLvl
/+ &NewValue as Integer +/
%This._treeLvl = &NewValue;
end-set;
get treeLvl
/+ Returns Integer +/
If %This._treeLvl = - 1 Then
Local string &tmp = %This.parentId;
If None(&tmp) Then
%This._treeLvl = 1;
Else
%This._treeLvl = %This.parent.treeLvl + 1;
End-If;
End-If;
Return %This._treeLvl;
end-get;
get childrenCount
/+ Returns Integer +/
Local integer &i, &_childrenCount;
&_childrenCount = &childrenArray.Len;
For &i = 1 To &childrenArray.Len
&_childrenCount = &_childrenCount + &childrenArray [&i].childrenCount;
End-For;
Return &_childrenCount;
end-get;
管理器:
import COMMON:UTIL:entity:TreeDataEntity;
class TreeDataManager
method TreeDataManager();
/* 获取节点 */
method getNodeById(&id As string) Returns COMMON:UTIL:entity:TreeDataEntity;
/* 获取父节点ID */
method getParentNodeId(&id As string) Returns string;
/* 获取父节点对象 */
method getParentNode(&id As string) Returns COMMON:UTIL:entity:TreeDataEntity;
/* 获取直属子节点对象数组 */A
method getChildrenArray(&id As string) Returns array of COMMON:UTIL:entity:TreeDataEntity;
/* 获取所有子节点对象数组 */
method getAllChildrenArray(&id As string) Returns array of COMMON:UTIL:entity:TreeDataEntity;
/* 获取直属子节点个数 */
method getChildrenCount(&id As string) Returns integer;
/* 获取所有子节点个数 */
method getAllChildrenCount(&id As string) Returns integer;
/* 判断A是否为B的子节点 */
method IsABsChild(&idA As string, &idB As string) Returns boolean;
/* 添加节点时父节点一定要存在。
此模式好处是:添加速度较快,取节点级别时速度较快。 */
method addTreeNode(&node As COMMON:UTIL:entity:TreeDataEntity);
/* 无序添加模式,可随意添加任何节点。
此模式好处是可以随意添加节点信息,比如当树结构表中无索引时,按序查询数据是比较慢的,可以在查询时不进行排序,使用此方法添加。
缺点是添加速度相对较慢,取节点级别时需要临时计算 */
method disorderAddTreeNode(&node As COMMON:UTIL:entity:TreeDataEntity);
rem method disorderAddTreeNode(&node As COMMON:UTIL:entity:TreeDataEntity);
/* 是否存在某个节点 */
method containsNode(&id As string) Returns boolean;
method clean();
private
method mergeNodeArray(&nodeA As array of COMMON:UTIL:entity:TreeDataEntity, &nodeB As array of COMMON:UTIL:entity:TreeDataEntity) Returns array of COMMON:UTIL:entity:TreeDataEntity;
/* 节点数据索引 */
instance JavaObject &hashMap;
end-class;
method TreeDataManager
%This.hashMap = CreateJavaObject("java.util.HashMap");
end-method;
method getNodeById
/+ &id as String +/
/+ Returns COMMON:UTIL:entity:TreeDataEntity +/
Return %This.hashMap.get(&id);
end-method;
method getParentNodeId
/+ &id as String +/
/+ Returns String +/
Return %This.getNodeById(&id).parentId;
end-method;
method getParentNode
/+ &id as String +/
/+ Returns COMMON:UTIL:entity:TreeDataEntity +/
Return %This.getNodeById(&id).parent;
end-method;
method getChildrenArray
/+ &id as String +/
/+ Returns Array of COMMON:UTIL:entity:TreeDataEntity +/
Return %This.hashMap.get(&id).childrenArray;
end-method;
method getAllChildrenArray
/+ &id as String +/
/+ Returns Array of COMMON:UTIL:entity:TreeDataEntity +/
Local array of COMMON:UTIL:entity:TreeDataEntity &result;
Local COMMON:UTIL:entity:TreeDataEntity &entityTemp;
Local COMMON:UTIL:entity:TreeDataEntity &entity = %This.hashMap.get(&id);
If &entity <> Null Then
&result = CreateArrayRept(&entityTemp, 0);
&entity.getAllChildren(&result);
End-If;
Return &result;
end-method;
method getChildrenCount
/+ &id as String +/
/+ Returns Integer +/
Return %This.hashMap.get(&id).childrenArray.len;
end-method;
method getAllChildrenCount
/+ &id as String +/
/+ Returns Integer +/
Return %This.hashMap.get(&id).childrenCount;
end-method;
method IsABsChild
/+ &idA as String, +/
/+ &idB as String +/
/+ Returns Boolean +/
Local COMMON:UTIL:entity:TreeDataEntity &tdme = %This.getNodeById(&idA);
Local integer &i;
For &i = 1 To 9999
If &tdme = Null Then
Break;
End-If;
If &tdme.parentId = &idB Then
Return True;
Else
&tdme = %This.getNodeById(&tdme.parentId);
End-If;
End-For;
Return False;
end-method;
method addTreeNode
/+ &node as COMMON:UTIL:entity:TreeDataEntity +/
%This.hashMap.put(&node.id, &node);
Local COMMON:UTIL:entity:TreeDataEntity &parentNote = %This.hashMap.get(&node.parentId);
If &parentNote <> Null Then
&parentNote.addChild(&node);
&node.parent = &parentNote;
&node.treeLvl = &parentNote.treeLvl + 1;
Else
&node.treeLvl = 1;
End-If;
end-method;
method disorderAddTreeNode
/+ &node as COMMON:UTIL:entity:TreeDataEntity +/
Local COMMON:UTIL:entity:TreeDataEntity &_node = %This.hashMap.get(&node.id);
If &_node <> Null Then /* 之前添加过此节点(可能是重复添加,也可能是之前为了维护树结构而创建的虚拟树节点),合并子节点 */
&_node.anyProperty = &node.anyProperty;
&_node.parentId = &node.parentId;
&_node.childrenArray = %This.mergeNodeArray(&node.childrenArray, &_node.childrenArray);
&node = &_node;
Else
%This.hashMap.put(&node.id, &node);
End-If;
/* 维护树结构数据 */
Local COMMON:UTIL:entity:TreeDataEntity &parentNote = %This.hashMap.get(&node.parentId);
If &parentNote = Null Then /* 如果此节点的父节点还没添加,那面就先创建一个空的父节点,后续添加时再补充信息 */
&parentNote = create COMMON:UTIL:entity:TreeDataEntity(&node.parentId, "");
%This.hashMap.put(&node.parentId, &parentNote);
Else
/* 父节点已经添加,检查一下树结构的此路径是否出现死循环,如果有,抛出异常 */
Local COMMON:UTIL:entity:TreeDataEntity &parentNote2 = &parentNote;
Local string &msg;
While &parentNote2 <> Null
If &parentNote2.id = &node.id Then /* 死循环 */
&msg = &node.id;
&parentNote2 = &parentNote;
While &parentNote2 <> Null
&msg = &msg | " → " | &parentNote2.id;
If &parentNote2.id = &node.id Then
throw CreateException(0, 0, &node.id | ":路径上存在死循环(" | &msg | ")");
End-If;
&parentNote2 = &parentNote2.parent;
End-While;
End-If;
&parentNote2 = &parentNote2.parent;
End-While;
End-If;
&parentNote.addChild(&node);
&node.parent = &parentNote;
rem End-If;
end-method;
method containsNode
/+ &id as String +/
/+ Returns Boolean +/
Return %This.hashMap.containsKey(&id);
end-method;
method mergeNodeArray
/+ &nodeA as Array of COMMON:UTIL:entity:TreeDataEntity, +/
/+ &nodeB as Array of COMMON:UTIL:entity:TreeDataEntity +/
/+ Returns Array of COMMON:UTIL:entity:TreeDataEntity +/
If &nodeA.Len = 0 Then
Return &nodeB;
End-If;
If &nodeB.Len = 0 Then
Return &nodeA;
End-If;
Local array of COMMON:UTIL:entity:TreeDataEntity &result, &passiveArray;
If &nodeA < &nodeB Then
&result = &nodeB;
&passiveArray = &nodeA;
Else
&result = &nodeA;
&passiveArray = &nodeB;
End-If;
Local integer &i;
For &i = 1 To &passiveArray.Len
&result.Push(&passiveArray [&i]);
End-For;
Return &result;
end-method;
method clean
%This.hashMap = Null;
end-method;
部门树实体类实现:
import T_COMMON:Util:TreeDataManagerEntity;
class DeptTreeBufferUtilEntrty extends T_COMMON:Util:TreeDataManagerEntity;
property Record deptTreeRecord;
method DeptTreeBufferUtilEntrty(&dr As Record, &tId As string, &pId As string);
end-class;
method DeptTreeBufferUtilEntrty
/+ &dr as Record, +/
/+ &tId as String, +/
/+ &pId as String +/
%Super = create T_COMMON:Util:TreeDataManagerEntity(&tId, &pId);
%This.deptTreeRecord = &dr;
end-method;
部门树管理器实现:
class DeptTreeBufferUtil extends T_COMMON:Util:TreeDataManager;
method DeptTreeBufferUtil(&effdt As date);
end-class;
method DeptTreeBufferUtil
/+ &effdt as Date +/
%Super = create T_COMMON:Util:TreeDataManager();
Local string &sqlString;
&sqlString = &sqlString | "SELECT * ";
&sqlString = &sqlString | " FROM (SELECT * ";
&sqlString = &sqlString | " FROM PSTREENODE N ";
&sqlString = &sqlString | " WHERE N.EFFDT = (SELECT MAX(E.EFFDT) ";
&sqlString = &sqlString | " FROM PSTREENODE E ";
&sqlString = &sqlString | " WHERE E.EFFDT <= %dateIn(:1) ";
&sqlString = &sqlString | " AND E.SETID = 'X20210609' ";
&sqlString = &sqlString | " AND E.TREE_NAME = 'DEPT_SECURITY')";
&sqlString = &sqlString | " AND N.SETID = 'X20210609' ";
&sqlString = &sqlString | " AND N.TREE_NAME = 'DEPT_SECURITY') PATH ";
&sqlString = &sqlString | " START WITH PATH.TREE_NODE = 'DP00001' ";
&sqlString = &sqlString | "CONNECT BY PRIOR PATH.TREE_NODE = PATH.PARENT_NODE_NAME ";
Local Record &deptTreeRecordTemp = CreateRecord(Record.PSTREENODE);
Local Record &deptTreeRecord;
Local SQL &sql = CreateSQL(&sqlString, &effdt);
Local T_COMMON:Tree:DeptTreeBufferUtilEntrty &dtEn;
While &sql.Fetch(&deptTreeRecordTemp)
&deptTreeRecord = CreateRecord(Record.PSTREENODE);
&deptTreeRecordTemp.CopyFieldsTo(&deptTreeRecord);
&dtEn = create T_COMMON:Tree:DeptTreeBufferUtilEntrty(&deptTreeRecord, &deptTreeRecord.TREE_NODE.Value, &deptTreeRecord.PARENT_NODE_NAME.Value);
%Super.addTreeNode(&dtEn);
End-While;
end-method;