多DTO映射实体时出现java.lang.StackOverflowError问题求助
问题根源:双向关联导致的无限递归栈溢出
你遇到的StackOverflowError是实体双向关联引发的无限递归映射导致的,和多对象同时映射无关,具体循环路径如下:
- 调用
NoteEntityToDtoGet映射Note时,会触发GroupEntityToDtoGet映射关联的Group - 在
GroupEntityToDtoGet方法中,又会调用NoteConvertList映射Group关联的所有Note列表 NoteConvertList会遍历每个Note并再次调用NoteEntityToDtoGet,形成无限循环,直到JVM栈内存耗尽
单独创建对象时正常,是因为创建场景使用的DTO(比如GroupCreationDTO)没有包含反向关联字段(比如没有notes列表),不会触发递归链。
修复方案
方案1:截断循环链,移除不必要的反向关联映射
如果业务不需要在GroupDTO中返回关联的Note列表,直接修改GroupEntityToDtoGet方法:
public GroupDTO GroupEntityToDtoGet(Group group) { GroupDTO groupDTO = new GroupDTO(); groupDTO.setId(group.getId()); groupDTO.setName(group.getName()); groupDTO.setUser(UserEntityToDtoGet(group.getUser())); groupDTO.setTags(TagConvertSet(group.getTags())); // 移除notes映射,截断递归循环 // groupDTO.setNotes(NoteConvertList(group.getNotes())); return groupDTO; }
方案2:为不同场景创建精简版DTO
如果需要在Group中展示Note信息,但不需要完整的NoteDTO(包含Group关联),可以创建仅含基础字段的精简DTO:
// 新增精简版NoteDTO public class NoteSimpleDTO { private Long id; private String title; private String description; // 省略getter/setter } // 在ObjectMapper中新增对应映射方法 public NoteSimpleDTO NoteEntityToSimpleDto(Note note) { NoteSimpleDTO dto = new NoteSimpleDTO(); dto.setId(note.getId()); dto.setTitle(note.getTitle()); dto.setDescription(note.getDescription()); return dto; } // 修改GroupEntityToDtoGet中的Note列表映射 public GroupDTO GroupEntityToDtoGet(Group group) { GroupDTO groupDTO = new GroupDTO(); groupDTO.setId(group.getId()); groupDTO.setName(group.getName()); groupDTO.setUser(UserEntityToDtoGet(group.getUser())); // 映射为精简版NoteDTO,避免反向关联Group groupDTO.setNotes(group.getNotes().stream() .map(this::NoteEntityToSimpleDto) .collect(Collectors.toList())); groupDTO.setTags(TagConvertSet(group.getTags())); return groupDTO; }
方案3:添加递归防护(适用于必须保留完整关联的场景)
用集合记录已处理的对象ID,避免重复映射:
// 重载映射方法,传入已处理ID集合 public NoteDTO NoteEntityToDtoGet(Note note, Set<Long> processedNoteIds) { if (processedNoteIds.contains(note.getId())) { // 已处理过,返回基础信息避免递归 NoteDTO dto = new NoteDTO(); dto.setId(note.getId()); dto.setTitle(note.getTitle()); return dto; } processedNoteIds.add(note.getId()); NoteDTO noteDTO = new NoteDTO(); noteDTO.setId(note.getId()); noteDTO.setTitle(note.getTitle()); noteDTO.setDescription(note.getDescription()); // 传递已处理集合给Group映射 noteDTO.setGroup(GroupEntityToDtoGet(note.getGroup(), processedNoteIds)); noteDTO.setTags(TagConvertSet(note.getTags())); return noteDTO; } // 重载Group映射方法 public GroupDTO GroupEntityToDtoGet(Group group, Set<Long> processedNoteIds) { GroupDTO groupDTO = new GroupDTO(); groupDTO.setId(group.getId()); groupDTO.setName(group.getName()); groupDTO.setUser(UserEntityToDtoGet(group.getUser())); // 映射Note时传入已处理集合 groupDTO.setNotes(group.getNotes().stream() .map(note -> NoteEntityToDtoGet(note, processedNoteIds)) .collect(Collectors.toList())); groupDTO.setTags(TagConvertSet(group.getTags())); return groupDTO; } // 对外暴露的方法,初始化集合 public NoteDTO NoteEntityToDtoGet(Note note) { return NoteEntityToDtoGet(note, new HashSet<>()); }
内容的提问来源于stack exchange,提问作者m_novak
相关产品推荐
相关产品推荐

