通过层级生成树形父子结构(Java)

该代码示例展示了如何处理导入的车辆配置信息,将信息拆分为多个层级并存储到数据库中。利用MyBatisPlus,数据被拆分成7个层级并保存在Set中,然后批量插入和更新到数据库的树形结构中,同时进行了事务管理和异步批量操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

思路梳理过程

  1. 以车辆配置信息为例,导入的模版信息如下,我的需求是固定为7个层级,不固定的也没关系

只需导入最后的层级即可,即存在/广汽本田/广汽本田/2018款/广汽本田/2018款/车系01时,只需要导入/广汽本田/2018款/车系01层级即可

导入层级信息

  1. 把导入的层级数据,根据列进行拆分,把拆分后的数据放入Set中保存,组合成所有层级

/广汽本田/2018款/车系01拆分成/广汽本田/广汽本田/2018款/广汽本田/2018款/车系01,分别存入Set中

  1. 根据Set集合,批量保存数据到数据库中(暂未设置父节点)
  2. 如序号1的图片所示,层级固定为7级,且顺序严格从左到右;从品牌开始,按顺序查找下一级的数据,按照层级查找子节点,批量更新(设置父节点数据)

代码实现

数据层工具是MyBatis Plus

相关实体

@Data
public class VehicleDictListImportDto {

    @ExcelProperty(value = "品牌", order = 0)
    private String brand;

    @ExcelProperty(value = "年款", order = 1)
    private String year;

    @ExcelProperty(value = "车系", order = 2)
    private String series;

    @ExcelProperty(value = "车型", order = 3)
    private String configuration;

    @ExcelProperty(value = "排量", order = 4)
    private String displacement;

    @ExcelProperty(value = "外观颜色", order = 5)
    private String appearanceColor;

    @ExcelProperty(value = "内饰颜色", order = 6)
    private String interiorColor;

}
public enum NodeTypeEnum {
    BRAND("BRAND", "品牌"),
    YEAR("YEAR", "年款"),
    SERIES("SERIES", "车系"),
    CONFIGURATION("CONFIGURATION", "车型"),
    DISPLACEMENT("DISPLACEMENT", "排量"),
    APPEARANCE_COLOR("APPEARANCE_COLOR", "外观颜色"),
    INTERIOR_COLOR("INTERIOR_COLOR", "内饰颜色");
}

/**
 * 树形节点数据
 */
@Data
@TableName(value = "tree_node")
public class TreeNodeEntity implements Serializable {

    private static final long serialVersionUID = 1L;

    @TableId(type = IdType.ASSIGN_UUID, value = "id")
    private String id;

    /**
     * 业务类型
     */
    @TableField
    private String attrCode;

    /**
     * 节点名称
     */
    @TableField
    private String name;

    /**
     * 节点编码
     */
    @TableField
    private String code;

    /**
     * 节点类型
     */
    @TableField
    private String type;

    /**
     * 节点排序
     */
    @TableField
    private Integer orderNum;

    /**
     * 父节点id
     */
    @TableField
    private String pId;

    @TableField("is_deleted")
    private Boolean deleted = false;

    /**
     * 节点名称层级
     */
    @TableField
    private String level;

}

Controller

@RequestExcel使用的是EasyExcel的starter,用于把导入的数据转成实体

@RequestMapping(value = "/importExcel", method = {RequestMethod.POST})
    public Result<Integer> importExcel(@RequestExcel(ignoreEmptyRow = true) List<VehicleDictListImportDto> importDtoList) {
        return Result.ok(treeNodeService.importExcel(importDtoList));
    }

Service

@Slf4j
@Service
public class TreeNodeService extends ServiceImpl<TreeNodeMapper, TreeNodeEntity> {
    @Resource
    private TreeNodeMapper treeNodeMapper;
    @Resource
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;

    public static final String FORWARD_SLASH = "/";

	@Transactional(rollbackFor = Exception.class)
    public Integer importExcel(List<VehicleDictListImportDto> importDtoList) {
        if (importDtoList.size() == 0) {
            return 0;
        }

        // 所有层级
        Set<String> levelSet = new HashSet<>();
        for (VehicleDictListImportDto importDto : importDtoList) {
            String brand = FORWARD_SLASH + importDto.getBrand();
            String year = FORWARD_SLASH + importDto.getYear();
            String series = FORWARD_SLASH + importDto.getSeries();
            String configuration = FORWARD_SLASH + importDto.getConfiguration();
            String displacement = FORWARD_SLASH + importDto.getDisplacement();
            String appearanceColor = FORWARD_SLASH + importDto.getAppearanceColor();
            String interiorColor = FORWARD_SLASH + importDto.getInteriorColor();

            levelSet.add(brand);
            levelSet.add(brand + year);
            levelSet.add(brand + year + series);
            levelSet.add(brand + year + series + configuration);
            levelSet.add(brand + year + series + configuration + displacement);
            levelSet.add(brand + year + series + configuration + displacement + appearanceColor);
            levelSet.add(brand + year + series + configuration + displacement + appearanceColor + interiorColor);
        }

        // 获取当前登陆者
        String userId = UserUtils.getUserId();
        // 获取执行器
        ThreadPoolExecutor threadPoolExecutor = threadPoolTaskExecutor.getThreadPoolExecutor();
        // 总level
        List<String> levelList = new ArrayList<>(levelSet);
        List<TreeNodeEntity> createList = new ArrayList<>();
        for (String level : levelList) {
            String name = StringUtils.substringAfterLast(level, FORWARD_SLASH);
            TreeNodeEntity treeNodeEntity = new TreeNodeEntity();
            treeNodeEntity.setAttrCode("NEW_VEHICLE");
            treeNodeEntity.setName(name);
            treeNodeEntity.setCode(name);
            treeNodeEntity.setType(this.getTypeByLevel(level));
            treeNodeEntity.setOrderNum(1000);
            treeNodeEntity.setLevel(level);
            treeNodeEntity.setCreateUser(userId);
            treeNodeEntity.setCreateTime(new Date());
            treeNodeEntity.setUpdateUser(userId);
            treeNodeEntity.setUpdateTime(new Date());
            createList.add(treeNodeEntity);
        }

        int number = 500;
        int createListSize = createList.size();
        int createThreadSize = (int) Math.ceil(1.0 * createListSize / number);
        // 异步批量新增数据
        List<CompletableFuture<Void>> createFutureList = new ArrayList<>();
        for (int i = 0; i < createThreadSize; i++) {
            List<TreeNodeEntity> createSubList = createList.subList(i * number, Math.min(i * number + number, createListSize));
            CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
                log.info("子线程-批量新增-开始:{}", Thread.currentThread().getName());
                super.saveBatch(createSubList);
                log.info("子线程-批量新增-结束:{}", Thread.currentThread().getName());
            }, threadPoolExecutor);
            createFutureList.add(future);
        }
        CompletableFuture<Void> createAllOf = CompletableFuture.allOf(createFutureList.toArray(new CompletableFuture[0]));
        createAllOf.join();
        log.info("主线程-新增操作已结束");

        // 节点类型集合
        Map<String, List<TreeNodeEntity>> typeMap = createList.stream()
                .collect(Collectors.groupingBy(TreeNodeEntity::getType));
        List<TreeNodeEntity> updateList = new ArrayList<>();
        NodeTypeEnum[] nodeTypeEnumArray = NodeTypeEnum.values();
        for (int i = 0; i < nodeTypeEnumArray.length - 1; i++) {
            NodeTypeEnum nodeTypeEnum = nodeTypeEnumArray[i];
            List<TreeNodeEntity> pidEntityList = typeMap.get(nodeTypeEnum.getValue());
            for (TreeNodeEntity pidEntity : pidEntityList) {
                String id = pidEntity.getId();
                String level = pidEntity.getLevel();
                // 防止找到仅左边相同的数据
                List<TreeNodeEntity> entityList = typeMap.get(nodeTypeEnumArray[i + 1].getValue()).stream()
                        .filter(x -> StringUtils.contains(x.getLevel(), level + FORWARD_SLASH))
                        .collect(Collectors.toList());
                for (TreeNodeEntity entity : entityList) {
                    entity.setPId(id);
                    updateList.add(entity);
                }
            }
        }
        // 异步批量修改数据
        int updateListSize = updateList.size();
        int updateThreadSize = (int) Math.ceil(1.0 * updateListSize / number);
        List<CompletableFuture<Void>> updateFutureList = new ArrayList<>();
        for (int i = 0; i < updateThreadSize; i++) {
            List<TreeNodeEntity> updateSubList = updateList.subList(i * number, Math.min(i * number + number, updateListSize));
            CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
                log.info("子线程-批量更新-开始:{}", Thread.currentThread().getName());
                super.updateBatchById(updateSubList);
                log.info("子线程-批量更新-结束:{}", Thread.currentThread().getName());
            }, threadPoolExecutor);
            updateFutureList.add(future);
        }
        CompletableFuture<Void> updateAllOf = CompletableFuture.allOf(updateFutureList.toArray(new CompletableFuture[0]));
        updateAllOf.join();
        log.info("主线程-更新操作已结束");

        return createListSize;
    }

	
    /**
     * 根据层级获取节点类型
     *
     * @param level 层级
     * @return 节点类型
     */
    private String getTypeByLevel(String level) {
        String type;
        int times = StringUtils.countMatches(level, FORWARD_SLASH);
        switch (times) {
            case 1:
                type = NodeTypeEnum.BRAND.getValue();
                break;
            case 2:
                type = NodeTypeEnum.YEAR.getValue();
                break;
            case 3:
                type = NodeTypeEnum.SERIES.getValue();
                break;
            case 4:
                type = NodeTypeEnum.CONFIGURATION.getValue();
                break;
            case 5:
                type = NodeTypeEnum.DISPLACEMENT.getValue();
                break;
            case 6:
                type = NodeTypeEnum.APPEARANCE_COLOR.getValue();
                break;
            case 7:
                type = NodeTypeEnum.INTERIOR_COLOR.getValue();
                break;
            default:
                throw new BizException("传入的层级不正确,错误的数据为:{}", level);
        }
        return type;
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值