I'm struggling with cycle dependency problem with MapStruct.
I keep having a StackOverFlow error due to circular dependencies.
To avoid it, I just need to exclude a property of a List.
I found this : https://github.com/mapstruct/mapstruct/issues/933
I deeply looked over the internet and I have been surprised that I couldn't find any full exemple showing a bi-directional DTO mapping with MapStruct (except using @Context CycleAvoidingMappingContext
, not working to me).
[EDIT]: I found a workaround thanks to MapStruct chat, I add it to EditorMapper
Here is my case, pretty common I guess : I have 2 DTOs referencing each other:
public class BookDTO {
private Long id;
private String title;
//... other properties
//@JsonManagedReference --> not necessary anymore
private EditorDTO editor;
}
public class EditorDTO {
private Long id;
private String name;
//...other properties
//@JsonBackReference --> not necessary anymore
private List< BookDTO > bookList;
}
And I need MapStruct to be able to exclude the property Editor from the BookList in Editor, and then avoid the infinite loop. Here is what I currently have as mappers:
@Mapper
public interface BookMapper {
BookMapper INSTANCE = Mappers.getMapper( BookMapper.class );
@Mapping( target = "editor.bookList", ignore = true)
BookDTO toDTO( BookEntity bookEntity );
@Named( "NoEditor" )
@Mapping(target = "editor", ignore = true)
BookDTO toDTONoEditor( BookEntity bookEntity );
List<BookDTO> toDTOList( List<BookEntity> bookEntityList );
@Named( "NoEditor" )
@IterableMapping(qualifiedByName="NoEditor")
List<BookDTO> toDTOListNoEditor( List<BookEntity> bookEntityList );
@Mapping( target = "editor.bookList", ignore = true)
BookEntity toEntity( BookDTO bookDTO );
List<BookEntity> toEntityList( List<BookDTO> bookDTOList );
}
@Mapper(uses = BookMapper.class)
public interface EditorMapper {
EditorMapper INSTANCE = Mappers.getMapper( EditorMapper.class );
@Named( "NoEditor" )
@Mapping(target = "bookList", qualifiedByName = "NoEditor")
EditorDTO toDTO( EditorEntity editorEntity );
@Named( "NoEditor" )
@IterableMapping(qualifiedByName="NoEditor")
List<EditorDTO> toDTOList( List< EditorEntity > editorEntityList );
EditorEntity toEntity( EditorDTO editorDTO );
List<EditorEntity> toEntityList( List< EditorDTO > editorDTOList );
}
[EDIT]: it now works but it's not 100% clean (please see the answer I posted for more details)
I also tried this kind of method in mappers, but it didn't have any effect on my pb.
BookDTO toDTO( BookEntity bookEntity, @Context CycleAvoidingMappingContext context );
Does anyone know what I'm doing wrong? THANKS A LOT! :)
[EDIT]: I add the solution for a bi-directional ManyToMany mapping too Thanks to https://gitter.im/mapstruct/mapstruct-users#, I have been able to get the solution. [EDIT]: I still had errors that I didn't realize. It's now corrected in this update. I had to : - add
uses
attribut toEditorMapper
:@Mapper(componentModel = "spring", uses = BookMapper.class)
- add alternatives methods liketoDTONoEditor
ortoDTOListNoEditor
inBookMapper
where I ignore theeditor
property. - map theses alternative methods inEditorMapper
- same for each circular dependencyHere is the solution:
BookDTO
EditorDTO
CategoryDTO
BookMapper
EditorMapper
CategoryMapper
This way, The circular dependency is broken before it comes to infinite loop. However, it is 99% satisfying, because the
Editor
andBook
objects aren't perfectly clean.Editor
contains the bookList, well. But each book inbookList
still contains a nulleditor
field. And vice versa for theBook
object. But it seems to be a De/Serialization problem, not a MapStruct one. Here are the Json resultingEditor
Book
Category
Hope this help :)