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
相关产品推荐
相关产品推荐

