组织机构是在开发过程中遇到的非常常见的一个功能,通常组织机构都有非常明确的层级或者说是等级关系,以树或者森林的形式展现的居多。
在查询组织机构时,常见的功能有根据某个机构查询其子机构、查询某个机构的父级机构,以及我在这里要介绍的,查询全部机构并返回层次结构。
查询全部机构并非难事,单独说返回层次结构,如果使用例如MongoDB数据库存储的话也不难实现,但是如果使用关系型数据库存储数据,同时想减少数据库的访问次数,就需要做一些工作了,例如一次访问拿回数据后,通过代码来组拼。
我这里使用的表结构如下所示

建表语句如下所示
CREATE TABLE `dept` (
`id` int(10) NOT NULL AUTO_INCREMENT COMMENT '主键id',
`code` varchar(10) DEFAULT NULL COMMENT '机构编码',
`name` varchar(50) DEFAULT NULL COMMENT '机构名称',
`deep` int(4) DEFAULT NULL COMMENT '机构所在层级',
`parid` int(10) DEFAULT NULL COMMENT '父级id',
`sort` int(4) DEFAULT NULL COMMENT '部门顺序',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8;
通过insert语句初始化数据:
INSERT INTO `dept` VALUES ('1', '01', '根目录', '1', '0', '1');
INSERT INTO `dept` VALUES ('2', '0101', '子一', '2', '1', '1');
INSERT INTO `dept` VALUES ('3', '0102', '子二', '2', '1', '2');
INSERT INTO `dept` VALUES ('4', '0103', '子三', '2', '1', '3');
INSERT INTO `dept` VALUES ('5', '010201', '孙一', '3', '3', '1');
INSERT INTO `dept` VALUES ('6', '010202', '孙二', '3', '3', '2');
INSERT INTO `dept` VALUES ('7', '010301', '孙三', '3', '4', '1');
INSERT INTO `dept` VALUES ('8', '01020101', '孙孙一', '4', '5', '1');
INSERT INTO `dept` VALUES ('9', '0102010101', 'sun孙孙一', '5', '8', '1');
INSERT INTO `dept` VALUES ('10', '0104', '子四', '2', '1', '4');
INSERT INTO `dept` VALUES ('11', '01020201', '孙孙二', '4', '6', '1');
使用sql语句 select * from dept order by deep desc, parid desc; 查询的结果如下所示

通过数据可以很容易看出这是一个深度为5的树,即组织机构只有一个根目录,存在5级部门,同时通过id和parid可以很容易判断出部门之间的父子关系,或者通过code字段的编码规则也可以得出结论(本篇文章没有用到code字段)。正是通过这条sql语句查询出全部的组织机构数据,同时制定了排序规则,使我们拿到的是一个按层级和父级节点分好类的一组数据,下面将通过代码,将已经完成分类的数据整合成有层次的json字符串结构。
数据返回的顺序是从最底层到最顶层,而通过parid字段可以找到父级机构,所以我将这个过程称为“找爸爸”,也就是找到每个数据的父级节点数据,并将自己作为父级节点的子级数据嵌套进去。当然了,如果仅仅是当前节点“找爸爸”还不够,正确的做法应该是“带着儿子找爸爸”,或者说是“拖家带口找爸爸”,也就是当前节点应该先获取到所有的子孙数据完成层次的构造,再将自己作为父级节点的子级节点嵌套进去。
构造的实体类如下(这里省略了get和set方法):
public class Dept {
private Integer id;
private String code;
private String name;
private Integer deep;
private Integer parid;
private Integer sort;
private List<Dept> child;
}
示例代码如下,mapper中使用的sql语句就是上文中举的例子:
List<Dept> deptList = ctmDeptMapper.findAll();
//记录已经处理过的节点所处的层级,初始化为-1
int initDeep = -1;
//记录已经处理过的节点的父级机构id,初始化为-1
int initParId = -1;
List<Dept> depts = new ArrayList<>();
//结果缓存数据,用于记录机构id,以及此机构对应的子级机构列表,最后的结果数据也会缓存到这里
Map<Integer, List<Dept>> listByParid = new HashMap<>();
//将第一个节点初始化到结果缓存中,否则会丢失该数据
Dept firstDept = deptList.get(0);
List<Dept> initDeptList = new ArrayList<>();
initDeptList.add(firstDept);
listByParid.put(firstDept.getParid(), initDeptList);
//带着儿子找爸爸
for(Dept dept : deptList){
int id = dept.getId();
int parid = dept.getParid();
int deep = dept.getDeep();
if(listByParid.containsKey(id)){
//当前节点存在若干子节点
//在hashmap中获取所有
List<Dept> childList = listByParid.get(id);
//将该节点的子树维护到当前数据中
dept.setChild(childList);
}
if(deep != initDeep){
//进入新的层级
if(!depts.isEmpty()){
//当前节点处于新的层级,说明depts中的数据就是一个完整的子树
List<Dept> oldChildren = new ArrayList<>(depts);
//这个子树的根节点就是上一次循环中访问的节点
//所以这个完整的字树的父级id也就是上一个节点的父级id,该数据缓存在initParId中,将父级id和子树放入缓存
listByParid.put(initParId, oldChildren);
//进入新的层级,那么一定有新的孩子,需要初始化一个list
depts = new ArrayList<>();
}
}else{
if(parid != initParId){
//当前节点位于新的分支,则说明depts中的数据是一个完整的子树,与进入新的层级时处理方法一致
List<Dept> oldChildren = new ArrayList<>(depts);
listByParid.put(initParId, oldChildren);
depts = new ArrayList<>();
}
}
depts.add(dept);
initDeep = deep;
initParId = parid;
}
List<Dept> result = new ArrayList<>();
//按照上述方法,会有若干节点缓存到名为depts的list中(亲兄弟队列)
//若只有一个节点,说明这个节点是根节点
//若存在若干节点,说明这些节点是兄弟关系,他们有一个共同的虚根节点(组织机构为森林结构)
int size = depts.size();
if(size == 1){
Dept dept = depts.get(0);
int deptId = dept.getId();
List<Dept> childList = listByParid.get(deptId);
if(childList != null && !childList.isEmpty()){
dept.setChild(listByParid.get(deptId));
}
result.add(dept);
}else{
result.addAll(depts);
}
return gson.toJson(result);
返回结果展示:
[{"id":1,"code":"01","name":"根目录","deep":1,"parid":0,"sort":1,"child":[{"id":2,"code":"0101","name":"子一","deep":2,"parid":1,"sort":1},{"id":3,"code":"0102","name":"子二","deep":2,"parid":1,"sort":2,"child":[{"id":5,"code":"010201","name":"孙一","deep":3,"parid":3,"sort":1,"child":[{"id":8,"code":"01020101","name":"孙孙一","deep":4,"parid":5,"sort":1,"child":[{"id":9,"code":"0102010101","name":"sun孙孙一","deep":5,"parid":8,"sort":1}]}]},{"id":6,"code":"010202","name":"孙二","deep":3,"parid":3,"sort":2,"child":[{"id":11,"code":"01020201","name":"孙孙二","deep":4,"parid":6,"sort":1}]}]},{"id":4,"code":"0103","name":"子三","deep":2,"parid":1,"sort":3,"child":[{"id":7,"code":"010301","name":"孙三","deep":3,"parid":4,"sort":1}]},{"id":10,"code":"0104","name":"子四","deep":2,"parid":1,"sort":4}]}]
网友评论