场景:递归查询是我们开发中很常见的,如果没有一个比较好的思路,这将会让我们很头疼。

我这里介绍一个查询部门的例子,希望能给你一些启发

部门sql

java 递归次数统计 java递归查询_java

java 递归次数统计 java递归查询_java_02

-- ----------------------------
-- 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;
View Code
这个表最主要的是level,parentId这两个字段,比如说一个顶级部门开发部(顶级部门默认parentId为0),id为1,parentld为0,level为0
然后开发部下面有后端开发,前端开发这些子部门。那这些子部门的parentId就为开发部的id 1 ,level就为他父level加父id,中间用逗号隔开。
之所以要设计成这种形式,是因为我们下面会用到。
SysDept.java
packagecom.mmall.model;importlombok.Builder;importjava.util.Date;@Builderpublic classSysDept {privateInteger id;privateString name;privateInteger parentId;privateString level;privateInteger seq;privateString remark;privateString operator;privateDate operateTime;privateString operateIp;publicSysDept() {
}publicSysDept(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;
}publicInteger getId() {returnid;
}public voidsetId(Integer id) {this.id =id;
}publicString getName() {returnname;
}public voidsetName(String name) {this.name = name == null ? null: name.trim();
}publicInteger getParentId() {returnparentId;
}public voidsetParentId(Integer parentId) {this.parentId =parentId;
}publicString getLevel() {returnlevel;
}public voidsetLevel(String level) {this.level = level == null ? null: level.trim();
}publicInteger getSeq() {returnseq;
}public voidsetSeq(Integer seq) {this.seq =seq;
}publicString getRemark() {returnremark;
}public voidsetRemark(String remark) {this.remark = remark == null ? null: remark.trim();
}publicString getOperator() {returnoperator;
}public voidsetOperator(String operator) {this.operator = operator == null ? null: operator.trim();
}publicDate getOperateTime() {returnoperateTime;
}public voidsetOperateTime(Date operateTime) {this.operateTime =operateTime;
}publicString getOperateIp() {returnoperateIp;
}public voidsetOperateIp(String operateIp) {this.operateIp = operateIp == null ? null: operateIp.trim();
}
}
View Code
这个是部门的表,但是我们通常返回给前端的不是这样的数据类型,我们需要new一个dto,DeptLevelDto.java
增加一个属性List      ,    另外再增加一个方法,让查询出来的dept对象转换成deptDto,因为他俩基本上字段相同。
DeptLevelDto.java
packagecom.mmall.dto;importcom.google.common.collect.Lists;importcom.mmall.model.SysDept;importlombok.Getter;importlombok.Setter;importlombok.ToString;importorg.springframework.beans.BeanUtils;importjava.util.List;@Getter
@Setter
@ToStringpublic class DeptLevelDto extendsSysDept {private List deptList =Lists.newArrayList();public staticDeptLevelDto adapt(SysDept dept) {
DeptLevelDto dto= newDeptLevelDto();
BeanUtils.copyProperties(dept, dto);returndto;
}
}

View Code

基本上架子搭起来了,下面开始实现我们的逻辑   树形部门

如何得到一个树形结构呢?递归,必须得。

首先我们需要一个特殊的结构类型,类似于Map>,为什么要这样的数据类型呢?因为我们要根据key为level,得到这个部门下面的子节点。

举个例子:打比方说,我现在一个开发部的顶级对象,然后我可以得到开发部子节点的level(开发部的level+id),然后我根据子节点的level得到开发部下面的子节点List,如果list不为null,我就把list子节点add到开发部,然后再让遍历子节点,还是从新走这个方法。这就是递归。

记住:递归一定要有结束条件,这里的结束条件就是查出的子节点不为null就跳出。

准备条件:

首先:我需要得到一个类似于Map>  ,这个用

Multimap levelDeptMap = ArrayListMultimap.create();  为什么用它?

然后还需要一个RootList  这个是我们最开始遍历用的。因为遍历肯定是遍历根节点嘛。然后再一个一个挖挖挖~~

然后递归就可以了。具体看代码

SysTreeService.java

java 递归次数统计 java递归查询_java

packagecom.mmall.service;importcom.google.common.collect.ArrayListMultimap;importcom.google.common.collect.Lists;importcom.google.common.collect.Multimap;importcom.mmall.dao.SysDeptMapper;importcom.mmall.dto.AclDto;importcom.mmall.dto.AclModuleLevelDto;importcom.mmall.dto.DeptLevelDto;importcom.mmall.model.SysDept;importcom.mmall.util.LevelUtil;importorg.apache.commons.collections.CollectionUtils;importorg.springframework.stereotype.Service;importjavax.annotation.Resource;importjava.util.Collections;importjava.util.Comparator;importjava.util.List;/*** Created by 敲代码的卡卡罗特* on 2018/3/27 20:46.*/@Servicepublic classSysTreeService {
@ResourceprivateSysDeptMapper sysDeptMapper;public ListdeptTree() {//得到全部的部门对象
List deptList =sysDeptMapper.getAllDept();//new一个返回前端的DeptLevelDto List
List dtoList =Lists.newArrayList();for(SysDept dept : deptList) {
DeptLevelDto dto=DeptLevelDto.adapt(dept);
dtoList.add(dto);
}returndeptListToTree(dtoList);
}public List deptListToTree(ListdeptLevelList) {//如果得到为null,直接返回null List
if(CollectionUtils.isEmpty(deptLevelList)) {returnLists.newArrayList();
}//new一个我们需要的结构类型
Multimap levelDeptMap =ArrayListMultimap.create();//new一个我们需要的rootList
List 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() {public intcompare(DeptLevelDto o1, DeptLevelDto o2) {return o1.getSeq() -o2.getSeq();}
});//递归生成树
transformDeptTree(rootList, LevelUtil.ROOT, levelDeptMap);returnrootList;
}public void transformDeptTree(List deptLevelList, String level, MultimaplevelDeptMap) {for (int i = 0; i < deptLevelList.size(); i++) {//遍历该层的每个元素
DeptLevelDto deptLevelDto =deptLevelList.get(i);//处理当前层级的数据
String nextLevel =LevelUtil.calculateLevel(level, deptLevelDto.getId());//处理下一层
List tempDeptList = (List) levelDeptMap.get(nextLevel);if(CollectionUtils.isNotEmpty(tempDeptList)) {//排序
Collections.sort(tempDeptList, deptSeqComparator);//设置下一层部门
deptLevelDto.setDeptList(tempDeptList);//进入到下一层处理
transformDeptTree(tempDeptList, nextLevel, levelDeptMap);
}
}
}//排序方法
public Comparator deptSeqComparator = new Comparator() {public intcompare(DeptLevelDto o1, DeptLevelDto o2) {return o1.getSeq() -o2.getSeq();}
};
}
View Code
LevelUtil.java
packagecom.mmall.util;importorg.apache.commons.lang3.StringUtils;public classLevelUtil {public final static String SEPARATOR = ".";public final static String ROOT = "0";//0//0.1//0.1.2//0.1.3//0.4public static String calculateLevel(String parentLevel, intparentId) {if(StringUtils.isBlank(parentLevel)) {returnROOT;
}else{returnStringUtils.join(parentLevel, SEPARATOR, parentId);
}
}
}

View Code

这样就ok了,重要的是思想。如果觉得好,请点个赞,推荐一下,右侧打赏一下更好了。~~~