java 高效递归查询树 find_in_set 处理递归树
建表语句
DROP TABLE IF EXISTS `sys_dept`; CREATE TABLE `sys_dept` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '部门id', `parent_id` bigint(20) DEFAULT '0' COMMENT '父部门id', `ancestors` varchar(256) DEFAULT '' COMMENT '祖级列表', `dept_name` varchar(64) DEFAULT '' COMMENT '部门名称', `order_num` int(4) DEFAULT '0' COMMENT '显示顺序', `status` tinyint(1) DEFAULT '0' COMMENT '部门状态(0正常 1停用)', `create_time` datetime DEFAULT NULL COMMENT '创建时间', `create_user_id` bigint(20) DEFAULT NULL COMMENT '创建人id', `create_user_name` varchar(64) DEFAULT NULL COMMENT '创建人姓名', `update_time` datetime DEFAULT NULL COMMENT '修改时间', `update_user_id` bigint(20) DEFAULT NULL COMMENT '修改人id', `update_user_name` varchar(64) DEFAULT NULL COMMENT '修改人姓名', PRIMARY KEY (`id`) USING BTREE, UNIQUE KEY `uk_dept_name` (`parent_id`,`dept_name`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8 COMMENT='部门表'; -- ---------------------------- -- Records of sys_dept -- ---------------------------- BEGIN; INSERT INTO `sys_dept` VALUES (1, 0, '0', 'XX科技', 0, 0, '2021-10-09 10:31:47', 1, '系统管理员', NULL, NULL, NULL); INSERT INTO `sys_dept` VALUES (2, 1, '0,1', '商务部', 3, 0, '2021-12-06 15:33:20', 1, '系统管理员', '2021-12-06 15:58:30', 1234, '管理'); INSERT INTO `sys_dept` VALUES (3, 1, '0,1', 'XX关务部', 4, 0, '2021-12-06 15:37:01', 1, '系统管理员', '2021-12-06 15:40:15', 1, '系统管理员'); INSERT INTO `sys_dept` VALUES (4, 1, '0,1', 'XX货主', 6, 0, '2021-12-06 15:38:34', 1, '系统管理员', '2021-12-06 17:17:27', 1, '系统管理员'); INSERT INTO `sys_dept` VALUES (5, 1, '0,1', '技术部', 1, 0, '2021-12-06 15:39:22', 1, '系统管理员', '2021-12-06 15:57:48', 1234, '管理'); INSERT INTO `sys_dept` VALUES (6, 4, '0,1,4', '货主-A', 1, 0, '2021-12-06 16:15:26', 1234, '小A', '2021-12-06 17:16:33', 1, '系统管理员'); INSERT INTO `sys_dept` VALUES (7, 4, '0,1,4', '货主-B', 2, 0, '2021-12-06 16:15:39', 1234, '小A', '2021-12-06 17:17:37', 1, '系统管理员'); INSERT INTO `sys_dept` VALUES (8, 6, '0,1,4,6', '1', 11, 0, '2022-03-16 10:27:28', 1, '系统管理员', '2022-03-16 10:27:37', 1, '系统管理员'); INSERT INTO `sys_dept` VALUES (9, 8, '0,1,4,6,8', '111111', 0, 0, '2022-03-16 10:27:45', 1, '系统管理员', NULL, NULL, NULL); COMMIT;
新增
@Data @ApiModel(value = "部门编辑对象", description = "部门编辑请求对象")
public class DeptEditRequest { @ApiModelProperty(value = "主键id", example = "-1") @DecimalMin(value = "1", message = "角色id最小为1") private Long id; @ApiModelProperty(value = "父部门id", example = "-1") @NotNull(message = "上级部门" + HibernateConstant.NOT_NULL) @JsonProperty("parent_id") private Long parentId; @ApiModelProperty(value = "部门名称", example = "IT部") @NotBlank(message = "部门名称" + HibernateConstant.NOT_NULL) @JsonProperty("dept_name") private String deptName; @ApiModelProperty(value = "显示顺序", example = "0") @NotNull(message = "显示" + HibernateConstant.NOT_NULL) @JsonProperty("order_num") private Integer orderNum; @ApiModelProperty(value = "部门状态(0正常 1停用)", example = "0") @Min(value = 0, message = "部门状态(0正常 1停用)") @Max(value = 1, message = "部门状态(0正常 1停用)") private byte status; }
@ApiOperation("部门添加") @PostMapping("add") public JsonData add( @RequestBody DeptAddRequest request ) { return deptService.add(request); }
@Override public JsonData add(DeptAddRequest request) { DeptEditRequest deptEditRequest = new DeptEditRequest(); BeanUtils.copyProperties(request, deptEditRequest); //同级部门下,部门名称重复 checkDeptNameUnique(deptEditRequest); DeptDO deptDO = new DeptDO(); deptDO.setDeptName(request.getDeptName()); deptDO.setParentId(request.getParentId()); deptDO.setStatus(request.getStatus()); deptDO.setOrderNum(request.getOrderNum()); deptDO.setCreateTime(CommonUtil.getCurrentDate()); deptDO.setCreateUserId(CommonUtil.getCurrentUserId()); deptDO.setCreateUserName(CommonUtil.getCurrentUserName()); //检查上级部门状态 checkParentDeptState(deptDO); int rows = deptMapper.add(deptDO); if (rows > 0) { log.info("部门添加,rows:{},添加成功:{}", rows, deptDO); return JsonData.buildAddSuccess(); } return JsonData.buildError("添加失败"); } /** * 部门名称唯一值 * * @param request */ private void checkDeptNameUnique(DeptEditRequest request) { List<DeptDO> deptList = deptMapper.selectList( new LambdaQueryWrapper<DeptDO>() .eq(DeptDO::getDeptName, request.getDeptName()) .eq(DeptDO::getParentId, request.getParentId()) ); for (DeptDO deptDO : deptList) { if (deptDO.getId() != request.getId()) { throw new BizException(500, "部门已存在"); } } } /** * 检查上级部门状态 * * @param dept 部门对象 */ private void checkParentDeptState(DeptDO dept) { DeptDO deptDO = deptMapper.selectOne( new LambdaQueryWrapper<DeptDO>() .eq(DeptDO::getId, dept.getParentId()) ); if (deptDO == null) { throw new BizException(500, "上级部门不存在"); } if (1 == deptDO.getStatus()) { throw new BizException(500, "上级部门已停用,不允许新增"); } dept.setAncestors(deptDO.getAncestors() + "," + dept.getParentId()); }
编辑
@Data @ApiModel(value = "部门编辑对象", description = "部门编辑请求对象") public class DeptEditRequest { @ApiModelProperty(value = "主键id", example = "-1") @DecimalMin(value = "1", message = "角色id最小为1") private Long id; @ApiModelProperty(value = "父部门id", example = "-1") @NotNull(message = "上级部门" + HibernateConstant.NOT_NULL) @JsonProperty("parent_id") private Long parentId; @ApiModelProperty(value = "部门名称", example = "IT部") @NotBlank(message = "部门名称" + HibernateConstant.NOT_NULL) @JsonProperty("dept_name") private String deptName; @ApiModelProperty(value = "显示顺序", example = "0") @NotNull(message = "显示" + HibernateConstant.NOT_NULL) @JsonProperty("order_num") private Integer orderNum; @ApiModelProperty(value = "部门状态(0正常 1停用)", example = "0") @Min(value = 0, message = "部门状态(0正常 1停用)") @Max(value = 1, message = "部门状态(0正常 1停用)") private byte status; }
@ApiOperation("部门修改") @PostMapping("edit") public JsonData edit( @RequestBody DeptEditRequest request ) { if (request.getId() == 1) { throw new BizException(500, "不允许操作系统默认部门"); } return deptService.edit(request); }
@Override @Transactional public JsonData edit(DeptEditRequest request) { DeptDO deptDO = new DeptDO(); BeanUtils.copyProperties(request, deptDO); //同级部门下,部门名称重复 checkDeptNameUnique(request); //检查上级部门状态 checkParentDeptState(deptDO); //上级部门不能是自己 checkParentDept(request); //父级对象 DeptDO parentDept = deptMapper.selectOne( new LambdaQueryWrapper<DeptDO>() .eq(DeptDO::getId, deptDO.getParentId()) ); if (parentDept == null) { throw new BizException(500, "上级部门不存在"); } DeptDO oldDept = deptMapper.selectOne( new LambdaQueryWrapper<DeptDO>() .eq(DeptDO::getId, deptDO.getId()) ); if (oldDept == null) { throw new BizException(500, "记录不存在,请稍后重试"); } String newAncestors = parentDept.getAncestors() + "," + parentDept.getId(); String oldAncestors = oldDept.getAncestors(); deptDO.setAncestors(newAncestors); byte oldState = oldDept.getStatus(); int rows = deptMapper.updateById(deptDO); if (rows > 0) { log.info("部门修改,rows:{},修改成功:{}", rows, deptDO); //递归处理子部门状态 if (oldState != deptDO.getStatus()) { updateChildDeptState(deptDO, newAncestors, oldAncestors); } return JsonData.buildEditSuccess(); } return JsonData.buildError("修改失败"); } /** * 部门名称唯一值 * * @param request */ private void checkDeptNameUnique(DeptEditRequest request) { List<DeptDO> deptList = deptMapper.selectList( new LambdaQueryWrapper<DeptDO>() .eq(DeptDO::getDeptName, request.getDeptName()) .eq(DeptDO::getParentId, request.getParentId()) ); for (DeptDO deptDO : deptList) { if (deptDO.getId() != request.getId()) { throw new BizException(500, "部门已存在"); } } } /** * 检查上级部门状态 * * @param dept 部门对象 */ private void checkParentDeptState(DeptDO dept) { DeptDO deptDO = deptMapper.selectOne( new LambdaQueryWrapper<DeptDO>() .eq(DeptDO::getId, dept.getParentId()) ); if (deptDO == null) { throw new BizException(500, "上级部门不存在"); } if (1 == deptDO.getStatus()) { throw new BizException(500, "上级部门已停用,不允许新增"); } dept.setAncestors(deptDO.getAncestors() + "," + dept.getParentId()); } /** * 上级部门不能是自己 * * @param request */ private void checkParentDept(DeptEditRequest request) { if (request.getId().equals(request.getParentId())) { throw new BizException(500, "上级部门不能是自己"); } } /** * 处理子部门状态 * * @param deptDO 部门对象 * @param newAncestors 新的父ID集合 * @param oldAncestors 旧的父ID集合 */ private void updateChildDeptState(DeptDO deptDO, String newAncestors, String oldAncestors) { deptMapper.batchUpdateChildDeptState(deptDO.getId(), deptDO.getStatus()); //找所有子部门 List<DeptDO> deptList = deptMapper.selectChildrenDeptById(deptDO.getId()); for (DeptDO dpt : deptList) { dpt.setAncestors(dpt.getAncestors().replaceFirst(oldAncestors, newAncestors)); deptMapper.updateById(dpt); } }
/** * 批量更新子部门状态 * * @param id 部门id * @param state 状态 * @return */ int batchUpdateChildDeptState( @Param("id") Long id, @Param("state") Byte state ); <!-- 批量更新子部门状态 --> <update id="batchUpdateChildDeptState"> UPDATE sys_dept SET `status`=#{state} WHERE ancestors LIKE CONCAT('%',#{id},'%') </update> /** * 根据id找所有子部门 * * @param id * @return */ List<DeptDO> selectChildrenDeptById(@Param("id") Long id); <!-- 根据id找所有子部门 --> <select id="selectChildrenDeptById" resultMap="BaseResultMap"> SELECT id, parent_id, ancestors, dept_name, order_num, `status`, create_time, create_user_id, create_user_name, update_time, update_user_id, update_user_name FROM sys_dept WHERE FIND_IN_SET(#{id},ancestors) </select>
删除
@ApiOperation("部门删除") @GetMapping("remove") public JsonData remove( @ApiParam(value = "部门id", required = true) @RequestParam(value = "id", required = true) Long id ) { if (id == 1) { throw new BizException(500, "不允许操作系统默认部门"); } return deptService.remove(id); }
@Override public JsonData remove(Long id) { int rows = deptMapper.deleteById(id); if (rows > 0) { log.info("部门删除,rows:{},删除成功:{}", rows, id); //递归删除子部门 removeChildDept(id); return JsonData.buildRemoveSuccess(); } return JsonData.buildError("部门删除失败"); } /** * 删除子部门 * * @param parentId */ private void removeChildDept(Long parentId) { List<DeptDO> deptList = deptMapper.selectList( new LambdaQueryWrapper<DeptDO>() .eq(DeptDO::getParentId, parentId) ); for (DeptDO deptDO : deptList) { deptMapper.deleteById(deptDO.getId()); removeChildDept(deptDO.getId()); } }
递归树
@Data @ApiModel(value = "DeptTreeVo对象", description = "部门树对象信息") public class DeptTreeVo { @ApiModelProperty("节点id") @JsonSerialize(using = ToStringSerializer.class) private Long id; @ApiModelProperty("节点名称") private String label; @ApiModelProperty("子节点") @JsonInclude(JsonInclude.Include.NON_EMPTY) private List<DeptTreeVo> children; }
@ApiOperation("部门树") @GetMapping("deptTree") public JsonData<List<DeptTreeVo>> deptTree() { return deptService.deptTree(); }
private static final ThreadPoolExecutor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor( APS * 2, APS * 4, KEEP_ALIVE_TIME, TimeUnit.SECONDS, new LinkedBlockingDeque<>(256), new ThreadFactoryBuilder().setNameFormat("部门-pool-%d").build(), new ThreadPoolExecutor.CallerRunsPolicy() ); @Override public JsonData<List<DeptTreeVo>> deptTree() { LambdaQueryWrapper<DeptDO> wrapper = new LambdaQueryWrapper<DeptDO>(); wrapper.orderByAsc(DeptDO::getParentId, DeptDO::getOrderNum); List<DeptDO> deptList = deptMapper.selectList(wrapper); return buildDeptTree(deptList); } /** * 构建前端需要构建树结构 * * @param deptList 部门集合 * @return */ private JsonData<List<DeptTreeVo>> buildDeptTree(List<DeptDO> deptList) { List<DeptTreeVo> deptTreeList = new ArrayList<>(); List<DeptDO> rootDeptList = deptList.stream().filter(obj -> 1 == obj.getId()).collect(Collectors.toList()); CountDownLatch latch = new CountDownLatch(rootDeptList.size()); for (DeptDO deptDO : rootDeptList) { THREAD_POOL_EXECUTOR.execute(() -> { DeptTreeVo vo = new DeptTreeVo(); vo.setId(deptDO.getId()); vo.setLabel(deptDO.getDeptName()); buildChildrentDeptTree(deptList, deptDO.getId(), vo); deptTreeList.add(vo); latch.countDown(); }); } try { latch.await(); } catch (InterruptedException e) { log.error("构建部门树结构线程报错:{}", e); } return JsonData.buildSuccess(deptTreeList); } /** * 构建子部门树 * * @param deptList 部门集合 * @param parentId 父ID * @param vo 部门树 */ private void buildChildrentDeptTree(List<DeptDO> deptList, Long parentId, DeptTreeVo vo) { List<DeptDO> childList = deptList.stream().filter(obj -> parentId == obj.getParentId()).collect(Collectors.toList()); List<DeptTreeVo> childDeptTreeList = new ArrayList<>(); for (DeptDO deptDO : childList) { DeptTreeVo dtv = new DeptTreeVo(); dtv.setId(deptDO.getId()); dtv.setLabel(deptDO.getDeptName()); buildChildrentDeptTree(deptList, deptDO.getId(), dtv); childDeptTreeList.add(dtv); } vo.setChildren(childDeptTreeList); } @Data @TableName("sys_dept") @ApiModel(value = "SysDeptDO对象", description = "部门表") public class DeptDO implements Serializable { private static final long serialVersionUID = 1L; @ApiModelProperty(value = "部门id") @TableId(value = "id", type = IdType.AUTO) private Long id; @ApiModelProperty(value = "父部门id") private Long parentId; @ApiModelProperty(value = "祖级列表") private String ancestors; @ApiModelProperty(value = "部门名称") private String deptName; @ApiModelProperty(value = "显示顺序") private Integer orderNum; @ApiModelProperty(value = "部门状态(0正常 1停用)") private byte status; @ApiModelProperty(value = "创建时间") @TableField(value = "create_time", fill = FieldFill.INSERT) private Date createTime; @ApiModelProperty(value = "创建人id") @TableField(value = "create_user_id", fill = FieldFill.INSERT) private Long createUserId; @ApiModelProperty(value = "创建人姓名") @TableField(value = "create_user_name", fill = FieldFill.INSERT) private String createUserName; @ApiModelProperty(value = "修改时间") @TableField(value = "update_time", fill = FieldFill.UPDATE) private Date updateTime; @ApiModelProperty(value = "修改人id") @TableField(value = "update_user_id", fill = FieldFill.UPDATE) private Long updateUserId; @ApiModelProperty(value = "修改人姓名") @TableField(value = "update_user_name", fill = FieldFill.UPDATE) private String updateUserName; }
树通用工具类
/** * 递归深度 */ private static final int DEPTH = 20; /** * 集合转树 * @param treeList 集合 * @param rootId 父节点 * @param <T> */ public static <T extends TreeNode<T>> void list2Tree(List<T> treeList,Long rootId){ if (CollUtil.isEmpty(treeList)){ return; } if (rootId==null){ throw new Exception("根节点不能为空"); } List<T> rootList = getRoot(treeList, rootId); if (CollUtil.isNotEmpty(rootList)){ List<T> result = new LinkedList<>(); rootList.stream().forEach(t-> result.add(getChildNode(t,0,treeList))); treeList.clear(); treeList.addAll(rootList); } } private static <T extends TreeNode<T>> List<T> getRoot(List<T> treeList,Long rootId){ return treeList.stream().filter(t->Objects.equals(t.rootId(),rootId)).collect(Collectors.toList()); } private static <T extends TreeNode<T>> T getChildNode(T t,int depthCount,List<T> treeList){ if (depthCount>DEPTH){ throw new Exception("超过递归限制"); } List<T> childList = treeList.stream().filter(item -> Objects.equals(item.rootId(), t.nodeId())).collect(Collectors.toList()); if (CollUtil.isNotEmpty(childList)){ int tempCount = ++depthCount; childList.stream().forEach(child->t.childList().add(getChildNode(child,tempCount,treeList))); } return t; }
import java.util.List; public interface TreeNode<T> { /** * 节点id * @return */ Long nodeId(); /** * 父节点 * @return */ Long rootId(); /** * 子级列表 * @return */ List<T> childList(); }
/** * @description: 树VO * @author: alex * @create: 2024-03-16 15:46 */ @Data public class XxxxVO implements TreeNode<XxxxVO > { @ApiModelProperty("主键id") private Long id; @ApiModelProperty("父ID") private Long parentId; @ApiModelProperty("编码") private String code; @ApiModelProperty("名称") private String name; private List<XxxxVO> subList = new LinkedList<>(); @ApiModelProperty("是否尾节点") private boolean isLastNode; @Override public Long nodeId() { return getId(); } @Override public Long rootId() { return getParentId(); } @Override public List<XxxxVO> childList() { return this.subList; } public boolean isLastNode() { return this.subList.size() == 0; } }
使用
//数据库中查询出来列表 List<XxxxVO> treeList=new LinkedList<>(); //转换树 TreeUtil.listToTree(treeList,0L);