You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java递归/非递归实现文档目录结构复制(重置ID与父ID)

修复递归复制目录结构的Java函数

问题描述

数据库中有一张表,字段包含id、name、createdBy、type(folder或file)、parentId(关联id字段)。现有一段Java函数尝试复制指定目录结构,但无法正常工作,需要实现递归或非递归函数,生成新ID与父ID,完整复制目录结构(比如复制包含Folder2、Folder3、file1的Folder1,其中Folder3包含file2和file3,要在数据库生成完整副本)。

原代码如下:

public void copyFolderStructure(UUID original, UUID newParent) {
    User user = userService.getCurrentUser().get();
    Document document = documentService.getById(original);
    UUID newUuid = UUID.randomUUID();
    document.setId(newUuid);
    Document newDocPar = documentService.getById(newParent);

    System.out.println(newDocPar);

    document.setParent(newDocPar);
    document.setCreatedBy(user.getLogin());
    document.setLastModifiedBy(user.getLogin());
    String directory = user.getLogin();
    String destPath = directory + File.separator + UUID.randomUUID();
    document.setPath(destPath);

    Document newDocId = documentService.save(document);

    List<Document> documents = documentService.getAllByParent(documentService.getById(original));

    for(Document doc: documents) {
        if(doc.getRecordType() == RecordType.Folder) {
            copyFolderStructure(doc.getId(),newDocId.getId());
        } else {
            doc.setId(UUID.randomUUID());
            doc.setParent(documentService.getById(newDocId.getId()));
            doc.setCreatedBy(user.getLogin());
            doc.setLastModifiedBy(user.getLogin());
            String dir = user.getLogin();
            String Path = directory + File.separator + UUID.randomUUID();
            doc.setPath(Path);
            documentService.save(doc);
        }
    }

}

原代码问题分析

  • 直接修改原数据库对象:从documentService.getById(original)获取的是数据库实体,直接修改其id并保存会篡改原数据,而非创建新副本。
  • 重复数据库查询:循环中多次调用documentService.getById(newDocId.getId()),增加不必要的性能开销。
  • 路径变量混淆:文件处理时误用外层的directory变量,导致路径生成逻辑错误。
  • 无异常校验:未处理用户未登录、目标文件夹不存在等场景,易触发空指针异常。

修复后的递归实现

public void copyFolderStructure(UUID originalFolderId, UUID newParentId) {
    // 校验当前用户状态
    User currentUser = userService.getCurrentUser().orElseThrow(() -> new IllegalStateException("用户未登录"));
    String userLogin = currentUser.getLogin();

    // 校验原文件夹有效性
    Document originalFolder = documentService.getById(originalFolderId);
    if (originalFolder == null || originalFolder.getRecordType() != RecordType.Folder) {
        throw new IllegalArgumentException("指定ID不是有效的文件夹");
    }

    // 校验父文件夹有效性
    Document parentFolder = documentService.getById(newParentId);
    if (parentFolder == null) {
        throw new IllegalArgumentException("父文件夹ID无效");
    }

    // 创建新文件夹副本
    Document newFolder = new Document();
    newFolder.setName(originalFolder.getName());
    newFolder.setRecordType(RecordType.Folder);
    newFolder.setCreatedBy(userLogin);
    newFolder.setLastModifiedBy(userLogin);
    newFolder.setParent(parentFolder);
    newFolder.setPath(userLogin + File.separator + UUID.randomUUID());
    Document savedNewFolder = documentService.save(newFolder);

    // 递归处理子节点
    List<Document> children = documentService.getAllByParent(originalFolder);
    for (Document child : children) {
        if (child.getRecordType() == RecordType.Folder) {
            copyFolderStructure(child.getId(), savedNewFolder.getId());
        } else {
            // 创建文件副本
            Document newFile = new Document();
            newFile.setName(child.getName());
            newFile.setRecordType(RecordType.File);
            newFile.setCreatedBy(userLogin);
            newFile.setLastModifiedBy(userLogin);
            newFile.setParent(savedNewFolder);
            newFile.setPath(userLogin + File.separator + UUID.randomUUID());
            documentService.save(newFile);
        }
    }
}

关键改进点

  • 新建对象而非修改原数据:每次复制都创建Document新实例,避免篡改数据库原有记录。
  • 前置校验与异常抛出:增加用户、文件夹有效性校验,抛出明确异常便于排查问题。
  • 减少重复查询:缓存父文件夹对象,避免循环中重复调用数据库查询。
  • 清晰的变量逻辑:修正路径变量使用,避免层级混淆。

非递归迭代实现(避免栈溢出)

如果目录层级极深,递归可能引发栈溢出,可使用迭代方式:

public void copyFolderStructureIterative(UUID originalFolderId, UUID newParentId) {
    User currentUser = userService.getCurrentUser().orElseThrow(() -> new IllegalStateException("用户未登录"));
    String userLogin = currentUser.getLogin();

    // 校验原文件夹与父文件夹
    Document originalFolder = documentService.getById(originalFolderId);
    if (originalFolder == null || originalFolder.getRecordType() != RecordType.Folder) {
        throw new IllegalArgumentException("指定ID不是有效的文件夹");
    }
    Document parentFolder = documentService.getById(newParentId);
    if (parentFolder == null) {
        throw new IllegalArgumentException("父文件夹ID无效");
    }

    // 用栈存储待处理的原文件夹ID与对应新父ID
    Deque<Map.Entry<UUID, UUID>> stack = new ArrayDeque<>();
    Document rootNewFolder = createFolderCopy(originalFolder, parentFolder, userLogin);
    stack.push(new AbstractMap.SimpleEntry<>(originalFolderId, rootNewFolder.getId()));

    while (!stack.isEmpty()) {
        Map.Entry<UUID, UUID> entry = stack.pop();
        UUID originalParentId = entry.getKey();
        UUID newParentCurrentId = entry.getValue();

        // 处理当前节点的所有子项
        List<Document> children = documentService.getAllByParent(documentService.getById(originalParentId));
        for (Document child : children) {
            if (child.getRecordType() == RecordType.Folder) {
                Document newFolder = createFolderCopy(child, documentService.getById(newParentCurrentId), userLogin);
                stack.push(new AbstractMap.SimpleEntry<>(child.getId(), newFolder.getId()));
            } else {
                createFileCopy(child, documentService.getById(newParentCurrentId), userLogin);
            }
        }
    }
}

// 抽离文件夹复制逻辑,复用代码
private Document createFolderCopy(Document originalFolder, Document parentFolder, String userLogin) {
    Document newFolder = new Document();
    newFolder.setName(originalFolder.getName());
    newFolder.setRecordType(RecordType.Folder);
    newFolder.setCreatedBy(userLogin);
    newFolder.setLastModifiedBy(userLogin);
    newFolder.setParent(parentFolder);
    newFolder.setPath(userLogin + File.separator + UUID.randomUUID());
    return documentService.save(newFolder);
}

// 抽离文件复制逻辑,复用代码
private void createFileCopy(Document originalFile, Document parentFolder, String userLogin) {
    Document newFile = new Document();
    newFile.setName(originalFile.getName());
    newFile.setRecordType(RecordType.File);
    newFile.setCreatedBy(userLogin);
    newFile.setLastModifiedBy(userLogin);
    newFile.setParent(parentFolder);
    newFile.setPath(userLogin + File.separator + UUID.randomUUID());
    documentService.save(newFile);
}

非递归实现优势

  • 避免深层目录导致的StackOverflowError,适配极深层级的目录结构。
  • 逻辑更直观,便于调试和维护。

内容的提问来源于stack exchange,提问作者Rubin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 03:09:27