Eu estou lutando com o problema ciclo de dependência com MapStruct. Eu continuo a ter um erro de StackOverFlow devido às dependências circulares. Para evitá-lo, eu só preciso para excluir uma propriedade de um List. Eu encontrei esta: https://github.com/mapstruct/mapstruct/issues/933 eu profundamente olhou através da internet e eu ter ficado surpreso que eu não poderia encontrar qualquer exemple completo mostrando um mapeamento DTO bi-direcional com MapStruct (excepto usando @Context CycleAvoidingMappingContext
, não funciona para mim).
[EDIT]: Eu encontrei um workarround graças a MapStruct bate-papo, eu adicioná-lo à EditorMapper
Aqui é o meu caso, muito comum eu acho: Eu tenho 2 DTOs que fazem referência um ao outro:
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;
}
E eu preciso MapStruct para ser capaz de excluir a propriedade editor do Booklist em Editor, e em seguida, evitar o loop infinito. Aqui está o que eu tenho atualmente como mapeadores:
@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]: agora funciona, mas não é 100% limpo (consulte a resposta que eu postei para mais detalhes)
Eu também tentei este tipo de método em mapeadores, mas não tem qualquer efeito sobre a minha pb.
BookDTO toDTO( BookEntity bookEntity, @Context CycleAvoidingMappingContext context );
Alguém sabe o que estou fazendo de errado? MUITO OBRIGADO! :)
[EDIT]: Eu adicionar a solução para um mapeamento ManyToMany bi-direcional também Graças a https://gitter.im/mapstruct/mapstruct-users# , eu tenho sido capaz de obter a solução. Eu tive que: - adicionar uses
attribut para EditorMapper
: @Mapper(componentModel = "spring", uses = BookMapper.class)
- add métodos alternativos como toDTONoEditor
ou toDTOListNoEditor
em BookMapper
onde eu ignorar a editor
propriedade. - mapa teses métodos alternativos em EditorMapper
- mesmo para cada dependência circular
Aqui está a solução:
BookDTO
public class BookDTO {
private Long id;
private String title;
//... other properties
private EditorDTO editor;
private List< CategoryDTO > categoryList;
}
EditorDTO
public class EditorDTO {
private Long id;
private String name;
//...other properties
private List< BookDTO > bookList;
}
CategoryDTO
public class CategoryDTO {
private Long id;
private String category;
private List< BookDTO > bookList;
}
BookMapper
@Mapper(componentModel = "spring", uses = CategoryMapper.class)
public interface BookMapper {
@Named( "NoBook" )
@Mappings( {
@Mapping(target = "categoryList", qualifiedByName = "NoBook"),
@Mapping( target = "editor.bookList", ignore = true)
} )
BookDTO toDTO( BookEntity bookEntity );
@Named( "NoEditor" )
@Mapping(target = "editor", ignore = true)
BookDTO toDTONoEditor( BookEntity bookEntity );
@Named( "NoCategory" )
@Mapping(target = "categoryList", ignore = true)
BookDTO toDTONoCategory( BookEntity bookEntity );
@Named( "NoBook" )
@IterableMapping(qualifiedByName="NoBook")
List<BookDTO> toDTOList( List<BookEntity> bookEntityList );
@Named( "NoCategory" )
@IterableMapping(qualifiedByName="NoCategory")
List<BookDTO> toDTOListNoCategory( List<BookEntity> bookEntityList );
@Named( "NoEditor" )
@IterableMapping(qualifiedByName="NoEditor")
List<BookDTO> toDTOListNoEditor( List<BookEntity> bookEntityList );
@Named( "NoBook" )
@Mappings( {
@Mapping(target = "categoryList", qualifiedByName = "NoBook"),
@Mapping( target = "editor.bookList", ignore = true)
} )
BookEntity toEntity( BookDTO bookDTO );
@Named( "NoCategory" )
@Mapping(target = "categoryList", ignore = true)
BookEntity toEntityNoCategory( BookDTO bookDTO );
@Named( "NoBook" )
@IterableMapping(qualifiedByName="NoBook")
List<BookEntity> toEntityList( List<BookDTO> bookDTOList );
@Named( "NoCategory" )
@IterableMapping(qualifiedByName="NoCategory")
List<BookEntity> toEntityListNoCategory( List<BookDTO> bookDTOList );
}
EditorMapper
@Mapper(componentModel = "spring", uses = BookMapper.class)
public interface EditorMapper {
@Named( "NoEditor" )
@Mapping(target = "bookList", qualifiedByName = "NoEditor")
EditorDTO toDTO( EditorEntity editorEntity );
@Named( "NoCategory" )
@Mapping(target = "bookList", ignore = true)
EditorDTO toDTONoBook( EditorEntity editorEntity );
@Named( "NoEditor" )
@IterableMapping(qualifiedByName="NoEditor")
List<EditorDTO> toDTOList( List< EditorEntity > editorEntityList );
@Named( "NoCategory" )
@IterableMapping(qualifiedByName="NoCategory")
List<EditorDTO> toDTOListNoCategory( List< EditorEntity > editorEntityList );
EditorEntity toEntity( EditorDTO editorDTO );
List<EditorEntity> toEntityList( List< EditorDTO > editorDTOList );
}
CategoryMapper
@Mapper(componentModel = "spring",uses = BookMapper.class)
public interface CategoryMapper {
@Named( "NoCategory" )
@Mapping(target = "bookList", qualifiedByName = "NoCategory")
CategoryDTO toDTO( CategoryEntity categoryEntity );
@Named( "NoBook" )
@Mapping(target = "bookList", ignore = true)
CategoryDTO toDTONoBook( CategoryEntity categoryEntity );
@Named( "NoCategory" )
@IterableMapping(qualifiedByName="NoCategory")
List<CategoryDTO> toDTOList( List< CategoryEntity > categoryEntityList );
@Named( "NoBook" )
@IterableMapping(qualifiedByName="NoBook")
List<CategoryDTO> toDTOListNoBook( List< CategoryEntity > categoryEntityList );
@Named( "NoCategory" )
@Mapping(target = "bookList", qualifiedByName = "NoCategory")
CategoryEntity toEntity( CategoryDTO categoryDTO );
@Named( "NoBook" )
@Mapping(target = "bookList", ignore = true)
CategoryEntity toEntityNoBook( CategoryDTO categoryDTO );
List<CategoryEntity> toEntityList( List< CategoryDTO > categoryDTOList );
@Named( "NoBook" )
@IterableMapping(qualifiedByName="NoBook")
List<CategoryEntity> toEntityListNoBook( List< CategoryDTO > categoryDTOList );
}
Desta forma, a dependência circular é quebrado antes se trata de loop infinito. No entanto, é de 99% satisfazendo, porque o Editor
e Book
objetos não são perfeitamente limpo. Editor
contém a lista de livros, bem. Mas cada livro bookList
ainda contém um nulo editor
campo. E vice-versa para o Book
objecto. Mas parece ser um problema De / serialização, não um MapStruct um. Aqui são o resultante Json
editor
{
"id": 1,
"name": "Folio",
"coordinates": null,
"bookList": [
{
"id": 1,
"title": "Le cycle de Fondation, I : Fondation",
"categoryList": [
{
"id": 5,
"category": "LITERATURE&FICTION"
}
],
"language": "French",
"isbn": 2070360539,
"publicationDate": null,
"numberOfPages": 416,
"authorList": [],
"libraryList": [
{
"id": 2,
"name": "Library2",
"coordinates": null
},
{
"id": 1,
"name": "Library1",
"coordinates": null
}
],
"editor": null
}
]
}
Livro
{
"id": 1,
"title": "Le cycle de Fondation, I : Fondation",
"categoryList": [
{
"id": 5,
"category": "LITERATURE&FICTION",
"bookList": null
}
],
"language": "French",
"isbn": 2070360539,
"publicationDate": null,
"numberOfPages": 416,
"authorList": [],
"libraryList": [
{
"id": 2,
"name": "Library2",
"coordinates": null
},
{
"id": 1,
"name": "Library1",
"coordinates": null
}
],
"editor": {
"id": 1,
"name": "Folio",
"coordinates": null,
"bookList": null
}
}
Categoria
{
"id": 1,
"category": "CHILDREN",
"bookList": [
{
"id": 5,
"title": "Le petit prince",
"categoryList": null,
"language": "French",
"isbn": 9782070612758,
"publicationDate": null,
"numberOfPages": 120,
"authorList": [],
"libraryList": [
{
"id": 2,
"name": "Library2",
"coordinates": null
},
{
"id": 1,
"name": "Library1",
"coordinates": null
}
],
"editor": null
}
]
}
Espero que isso ajuda :)