java的递归查询大体思路
场景:递归查询是我们开发中很常见的,如果没有一个比较好的思路,这将会让我们很头疼。
我这里介绍一个查询部门的例子,希望能给你一些启发
部门sql
-- ---------------------------- -- Table structure for `sys_dept` -- ---------------------------- DROP TABLE IF EXISTS `sys_dept`; CREATE TABLE `sys_dept` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '部门id', `name` varchar(20) NOT NULL DEFAULT '' COMMENT '部门名称', `parent_id` int(11) NOT NULL DEFAULT '0' COMMENT '上级部门id', `level` varchar(200) NOT NULL DEFAULT '' COMMENT '部门层级', `seq` int(11) NOT NULL DEFAULT '0' COMMENT '部门在当前层级下的顺序,由小到大', `remark` varchar(200) DEFAULT '' COMMENT '备注', `operator` varchar(20) NOT NULL DEFAULT '' COMMENT '操作者', `operate_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '最后一次操作时间', `operate_ip` varchar(20) NOT NULL DEFAULT '' COMMENT '最后一次更新操作者的ip地址', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8mb4; -- ---------------------------- -- Records of `sys_dept` -- ---------------------------- BEGIN; INSERT INTO `sys_dept` VALUES ('1', '技术部', '0', '0', '1', '技术部', 'system', '2017-10-11 07:21:40', '127.0.0.1'), ('2', '后端开发', '1', '0.1', '1', '后端', 'system-update', '2017-10-12 07:56:16', '127.0.0.1'), ('3', '前端开发', '1', '0.1', '2', '', 'system-update', '2017-10-14 11:29:45', '127.0.0.1'), ('4', 'UI设计', '1', '0.1', '3', '', 'system', '2017-10-12 07:55:43', '127.0.0.1'), ('11', '产品部', '0', '0', '2', '', 'Admin', '2017-10-16 22:52:29', '0:0:0:0:0:0:0:1'), ('12', '客服部', '0', '0', '4', '', 'Admin', '2017-10-17 00:22:55', '0:0:0:0:0:0:0:1'); COMMIT;
这个表最主要的是level,parentId这两个字段,比如说一个顶级部门开发部(顶级部门默认parentId为0),id为1,parentld为0,level为0
然后开发部下面有后端开发,前端开发这些子部门。那这些子部门的parentId就为开发部的id 1 ,level就为他父level加父id,中间用逗号隔开。
之所以要设计成这种形式,是因为我们下面会用到。
SysDept.java
package com.mmall.model; import lombok.Builder; import java.util.Date; @Builder public class SysDept { private Integer id; private String name; private Integer parentId; private String level; private Integer seq; private String remark; private String operator; private Date operateTime; private String operateIp; public SysDept() { } public SysDept(Integer id, String name, Integer parentId, String level, Integer seq, String remark, String operator, Date operateTime, String operateIp) { this.id = id; this.name = name; this.parentId = parentId; this.level = level; this.seq = seq; this.remark = remark; this.operator = operator; this.operateTime = operateTime; this.operateIp = operateIp; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name == null ? null : name.trim(); } public Integer getParentId() { return parentId; } public void setParentId(Integer parentId) { this.parentId = parentId; } public String getLevel() { return level; } public void setLevel(String level) { this.level = level == null ? null : level.trim(); } public Integer getSeq() { return seq; } public void setSeq(Integer seq) { this.seq = seq; } public String getRemark() { return remark; } public void setRemark(String remark) { this.remark = remark == null ? null : remark.trim(); } public String getOperator() { return operator; } public void setOperator(String operator) { this.operator = operator == null ? null : operator.trim(); } public Date getOperateTime() { return operateTime; } public void setOperateTime(Date operateTime) { this.operateTime = operateTime; } public String getOperateIp() { return operateIp; } public void setOperateIp(String operateIp) { this.operateIp = operateIp == null ? null : operateIp.trim(); } }
这个是部门的表,但是我们通常返回给前端的不是这样的数据类型,我们需要new一个dto,DeptLevelDto.java
增加一个属性List<DeptLevelDto> , 另外再增加一个方法,让查询出来的dept对象转换成deptDto,因为他俩基本上字段相同。
DeptLevelDto.java
package com.mmall.dto; import com.google.common.collect.Lists; import com.mmall.model.SysDept; import lombok.Getter; import lombok.Setter; import lombok.ToString; import org.springframework.beans.BeanUtils; import java.util.List; @Getter @Setter @ToString public class DeptLevelDto extends SysDept { private List<DeptLevelDto> deptList = Lists.newArrayList(); public static DeptLevelDto adapt(SysDept dept) { DeptLevelDto dto = new DeptLevelDto(); BeanUtils.copyProperties(dept, dto); return dto; } }
基本上架子搭起来了,下面开始实现我们的逻辑 树形部门
如何得到一个树形结构呢?递归,必须得。
首先我们需要一个特殊的结构类型,类似于Map<String,List<Dept>>,为什么要这样的数据类型呢?因为我们要根据key为level,得到这个部门下面的子节点。
举个例子:打比方说,我现在一个开发部的顶级对象,然后我可以得到开发部子节点的level(开发部的level+id),然后我根据子节点的level得到开发部下面的子节点List,如果list不为null,我就把list子节点add到开发部,然后再让遍历子节点,还是从新走这个方法。这就是递归。
记住:递归一定要有结束条件,这里的结束条件就是查出的子节点不为null就跳出。
准备条件:
首先:我需要得到一个类似于Map<String,List<Dept>> ,这个用
Multimap<String, DeptLevelDto> levelDeptMap = ArrayListMultimap.create(); 为什么用它?
然后还需要一个RootList<Dept> 这个是我们最开始遍历用的。因为遍历肯定是遍历根节点嘛。然后再一个一个挖挖挖~~
然后递归就可以了。具体看代码
SysTreeService.java
package com.mmall.service; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Lists; import com.google.common.collect.Multimap; import com.mmall.dao.SysDeptMapper; import com.mmall.dto.AclDto; import com.mmall.dto.AclModuleLevelDto; import com.mmall.dto.DeptLevelDto; import com.mmall.model.SysDept; import com.mmall.util.LevelUtil; import org.apache.commons.collections.CollectionUtils; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.util.Collections; import java.util.Comparator; import java.util.List; /** * Created by 敲代码的卡卡罗特 * on 2018/3/27 20:46. */ @Service public class SysTreeService { @Resource private SysDeptMapper sysDeptMapper; public List<DeptLevelDto> deptTree() { //得到全部的部门对象 List<SysDept> deptList = sysDeptMapper.getAllDept(); //new一个返回前端的DeptLevelDto List List<DeptLevelDto> dtoList = Lists.newArrayList(); for (SysDept dept : deptList) { DeptLevelDto dto = DeptLevelDto.adapt(dept); dtoList.add(dto); } return deptListToTree(dtoList); } public List<DeptLevelDto> deptListToTree(List<DeptLevelDto> deptLevelList) { //如果得到为null,直接返回null List if (CollectionUtils.isEmpty(deptLevelList)) { return Lists.newArrayList(); } //new一个我们需要的结构类型 Multimap<String, DeptLevelDto> levelDeptMap = ArrayListMultimap.create(); //new一个我们需要的rootList List<DeptLevelDto> rootList = Lists.newArrayList(); //遍历上面所有的list部门对象,把level==0的放到rootList 同时也可以利用Multimap的特性得到key为level , 值为对象的 数据结构 for (DeptLevelDto dto : deptLevelList) { levelDeptMap.put(dto.getLevel(), dto); if (LevelUtil.ROOT.equals(dto.getLevel())) { rootList.add(dto); } } // 按照seq从小到大排序 Collections.sort(rootList, new Comparator<DeptLevelDto>() { public int compare(DeptLevelDto o1, DeptLevelDto o2) { return o1.getSeq() - o2.getSeq(); } }); // 递归生成树 transformDeptTree(rootList, LevelUtil.ROOT, levelDeptMap); return rootList; } public void transformDeptTree(List<DeptLevelDto> deptLevelList, String level, Multimap<String, DeptLevelDto> levelDeptMap) { for (int i = 0; i < deptLevelList.size(); i++) { // 遍历该层的每个元素 DeptLevelDto deptLevelDto = deptLevelList.get(i); // 处理当前层级的数据 String nextLevel = LevelUtil.calculateLevel(level, deptLevelDto.getId()); // 处理下一层 List<DeptLevelDto> tempDeptList = (List<DeptLevelDto>) levelDeptMap.get(nextLevel); if (CollectionUtils.isNotEmpty(tempDeptList)) { // 排序 Collections.sort(tempDeptList, deptSeqComparator); // 设置下一层部门 deptLevelDto.setDeptList(tempDeptList); // 进入到下一层处理 transformDeptTree(tempDeptList, nextLevel, levelDeptMap); } } } //排序方法 public Comparator<DeptLevelDto> deptSeqComparator = new Comparator<DeptLevelDto>() { public int compare(DeptLevelDto o1, DeptLevelDto o2) { return o1.getSeq() - o2.getSeq(); } }; }
LevelUtil.java
package com.mmall.util; import org.apache.commons.lang3.StringUtils; public class LevelUtil { public final static String SEPARATOR = "."; public final static String ROOT = "0"; // 0 // 0.1 // 0.1.2 // 0.1.3 // 0.4 public static String calculateLevel(String parentLevel, int parentId) { if (StringUtils.isBlank(parentLevel)) { return ROOT; } else { return StringUtils.join(parentLevel, SEPARATOR, parentId); } } }
这样就ok了,重要的是思想。如果觉得好,请点个赞,推荐一下,右侧打赏一下更好了。~~~