Java获取树形结构数据

目录

前言:

开发前准备:

数据库:

实体类:

VO对象:

代码实现:

Controller层:

Service层:

运行结果:

第二种

前言:

在日常的开发或者工作需求中,我们会用到树形结构数据。树形结构是一个比较常用的数据类型,一般多用于查询包含父子类关系的数据。我们常常通过父级id和层级作为标识,是数据更加形象,从而进行树形数据的展示。

开发前准备:

数据库:

-- ----------------------------
-- Table structure for dev_type
-- ----------------------------
DROP TABLE IF EXISTS `dev_type`;
CREATE TABLE `dev_type`  (
  `id` int(50) NOT NULL,
  `name` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '名称',
  `level` tinyint(3) NULL DEFAULT NULL COMMENT '层级 1 2 3',
  `parent_id` int(50) NULL DEFAULT NULL COMMENT '父级id',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of dev_type
-- ----------------------------
INSERT INTO `dev_type` VALUES (110, '长安区', 3, 119);
INSERT INTO `dev_type` VALUES (119, '石家庄市', 2, 10086);
INSERT INTO `dev_type` VALUES (10010, '中原区', 3, 456789);
INSERT INTO `dev_type` VALUES (10086, '河北省', 1, NULL);
INSERT INTO `dev_type` VALUES (12313, '高新区', 3, 456789);
INSERT INTO `dev_type` VALUES (123456, '河南省', 1, NULL);
INSERT INTO `dev_type` VALUES (456789, '郑州市', 2, 123456);

SET FOREIGN_KEY_CHECKS = 1;

实体类:

@Data
@TableName("dev_type")
@JsonInclude(JsonInclude.Include.NON_EMPTY)
public class DevType  {

    private static final long serialVersionUID = 1L;
	@JsonSerialize(using = ToStringSerializer.class)
  private Long id;
    /**
     * 名称
     */
    @ApiModelProperty("名称")
    private String name;
    /**
     * 等级(1 ,2,3)
     */
    @ApiModelProperty("等级(1 ,2,3)")
    private String level;
    /**
     * 父级id
     */
    @ApiModelProperty("父级id")
    private Long parentId;

}

VO对象:

@Data
@JsonInclude(JsonInclude.Include.NON_EMPTY)
public class TreeNode extends DevType {


    private static final long serialVersionUID = 1L;

    @ApiModelProperty("子节点")
    private List<TreeNode> child;
}

代码实现:

Controller层:

	@GetMapping("/newTree/list")
	@ApiOperation(value = "树形结构列表", notes = "传入devType")
	public List<TreeNode> newTreeList(DevType devType) {
		List<TreeNode> pages = devTypeService.newTreeList(devType);
		return pages;
	}

Service层:

/**
	 * id和 parentId 有对应关系  父级id 为子级的 parentId
	 * 根据level也进行区分层级
	 * 什么也不传 按照树形结构查出所有数据
	 * @param devType
	 * @return
	 */
	@Override
	public List<TreeNode> newTreeList(DevType devType) {
		if (devType == null || (devType.getLevel() == null && devType.getId() == null && devType.getParentId() == null)) {
			devType.setLevel("1");
		}
		QueryWrapper<DevType> queryWrapper = new QueryWrapper<>();
		// 构造查询条件
		queryWrapper.eq(devType.getId() != null, "id", devType.getId())
				.eq(StringUtils.isNotBlank(devType.getLevel()), "level", devType.getLevel())
				.eq(devType.getParentId() != null, "parent_id", devType.getParentId());

		return getTree(queryWrapper);
	}

	private List<TreeNode> getTree(QueryWrapper<DevType> queryWrapper) {

		List<DevType> list = this.list(queryWrapper);
		List<TreeNode> voList = new ArrayList<>(list.size());
		for (DevType child : list) {
			TreeNode childVO = new TreeNode();
			BeanUtils.copyProperties(child, childVO);

			QueryWrapper<DevType> childQueryWrapper = new QueryWrapper<>();
			childQueryWrapper.eq("parent_id", child.getId());
			childVO.setChild(getTree(childQueryWrapper));

			voList.add(childVO);
		}
		return voList;
	}

运行结果:

[
    {
        "id": "10086",
        "name": "河北省",
        "level": "1",
        "child": [
            {
                "id": "119",
                "name": "石家庄市",
                "level": "2",
                "parentId": 10086,
                "child": [
                    {
                        "id": "110",
                        "name": "长安区",
                        "level": "3",
                        "parentId": 119
                    }
                ]
            }
        ]
    },
    {
        "id": "123456",
        "name": "河南省",
        "level": "1",
        "child": [
            {
                "id": "456789",
                "name": "郑州市",
                "level": "2",
                "parentId": 123456,
                "child": [
                    {
                        "id": "10010",
                        "name": "中原区",
                        "level": "3",
                        "parentId": 456789
                    },
                    {
                        "id": "12313",
                        "name": "高新区",
                        "level": "3",
                        "parentId": 456789
                    }
                ]
            }
        ]
    }
]

第二种:

    @GetMapping("/tree/list")
	@ApiOperationSupport(order = 2)
	@ApiOperation(value = "树形结构列表", notes = "")
	public R<List<TreeNode>> getDataList() {
		List<DevType> pages = devTypeService.list();
		List<TreeNode> devTypes = buildTree(pages, null);
		return R.data(devTypes);
	}

    private List<TreeNode> buildTree(List<DevType> devTypes, Long parentId) {
		List<TreeNode> tree = new ArrayList<>();

		for (DevType devType : devTypes) {
			if (devType.getParentId() == parentId) {
				TreeNodetreeNode = new TreeNode();
				BeanUtils.copyProperties(devType,treeNode);
				// 递归构建子树
				List<TreeNode> children = buildTree(devTypes, devType.getId());
				treeNode.setChildren(children);
				tree.add(treeNode);
			}
		}

		return tree;
	}

第三种:

通过stream获取: 

	/**
	 * 使用stream获取树形结构
	 * @return
	 */
	@GetMapping("/new/tree")
	@ApiOperationSupport(order = 2)
	@ApiOperation(value = "树形结构列表", notes = "传入devType")
	public R<List<TreeNode>> newTree() {
		List<DevType> pages = devTypeService.list();			//1
		List<TreeNode> list = pages.stream().map(devType -> {
			TreeNode vo = new TreeNode();
			BeanUtils.copyProperties(devType, vo);
			return vo;
		}).collect(Collectors.toList());						//2
		//1、2 两部可以通过mapper方法实现,直接转换成VO数据
		List<TreeNode> TreeMenu = list.stream().filter(m -> m.getParentId() == null).map(
				(m) -> {
					m.setChildren(getChildrens(m, list));
					return m;
				}
		).collect(Collectors.toList());
		return R.data(TreeMenu);
	}

	private List<TreeNode> getChildrens(TreeNode root, List<TreeNode> all) {

		List<TreeNode> children = all.stream().filter(m -> {
			return Objects.equals(m.getParentId(), root.getId());
		}).map(
				(m) -> {
					m.setChildren(getChildrens(m, all));
					return m;
				}
		).collect(Collectors.toList());
		return children;
	}

         在这里,我们需要先搞清楚对应的父子关系,只有捋顺了关系之后才能进行代码的书写。第一种方式是我们什么都不传的情况下,默认查出的是全部的数据。如果想要查某一条下面具体的子类,可以传入对应的id和层级level。第二种是查出全部的树形结构数据。在此特作分享,给大家提供不一样的思路。

/** * 根据等级查询类目树 * * @param level * @return */ @Override public List queryCategoryTree(Integer level) { //查询当前级别下类目 List list = categoryDAO.list(level); //组装好的类目树,返回前端 List categoryTree = new ArrayList(); //所有类目 List allDTOList = new ArrayList(); if (CollectionUtils.isEmpty(list)) { return categoryTree; } for (CategoryDO categoryDO : list) { allDTOList.add(new CategoryTreeDTO().convertDOToDTO(categoryDO)); } //当前等级类目 categoryTree = allDTOList.stream().filter(dto -> level.equals(dto.getLevel())).collect(Collectors.toList()); for (CategoryTreeDTO categoryTreeDTO : categoryTree) { //组装类目为树结构 assembleTree(categoryTreeDTO, allDTOList,Constants.CATEGORY_MAX_LEVEL - level); } return categoryTree; } /** * 组装树 * * @param categoryTreeDTO * @param allList * @param remainRecursionCount 剩余递归次数 * @return */ public CategoryTreeDTO assembleTree(CategoryTreeDTO categoryTreeDTO, List allList, int remainRecursionCount) { remainRecursionCount--; //最大递归次数不超过Constants.CATEGORY_MAX_LEVEL-level次,防止坏数据死循环 if(remainRecursionCount < 0){ return categoryTreeDTO; } String categoryCode = categoryTreeDTO.getCategoryCode(); Integer level = categoryTreeDTO.getLevel(); //到达最后等级树返回 if (Constants.CATEGORY_MAX_LEVEL == level) { return categoryTreeDTO; } //子类目 List child = allList.stream().filter(a -> categoryCode.equals(a.getParentCode())).collect(Collectors.toList()); if (null == child) { return categoryTreeDTO; } categoryTreeDTO.setChildren(child); //组装子类目 for (CategoryTreeDTO dto : child) { assembleTree(dto, allList,remainRecursionCount); } return categoryTreeDTO; }
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值