我正在努力解决 MapStruct 的循环依赖问题。 由于循环依赖,我一直遇到 StackOverFlow 错误。 为了避免这种情况,我只需要排除列表的一个属性。 我发现了这个:https://github.com/mapstruct/mapstruct/issues/933 我深入浏览了互联网,令我惊讶的是我找不到任何完整的示例来显示与 MapStruct 的双向 DTO 映射(除了使用 @Context CycleAvoidingMappingContext ,对我不起作用)。
[编辑]:通过 MapStruct 聊天,我找到了解决方法,我将其添加到 EditorMapper
这是我的情况,我猜很常见: 我有 2 个相互引用的 DTO:
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;
}
而且我需要 MapStruct 能够从编辑器中的 BookList 中排除属性 Editor,然后避免无限循环。 以下是我目前作为映射器所拥有的:
@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 );
}
[编辑]:它现在可以工作,但不是 100% 干净(请参阅我发布的答案以了解更多详细信息)
我也在mappers中尝试过这种方法,但对我的pb没有任何影响。
BookDTO toDTO( BookEntity bookEntity, @Context CycleAvoidingMappingContext context );
有人知道我做错了什么吗?多谢! :)
最佳答案
[编辑]:我也添加了双向 ManyToMany 映射的解决方案
感谢https://gitter.im/mapstruct/mapstruct-users# ,我已经能够得到解决方案。
[编辑]:我仍然有一些我没有意识到的错误。此更新现已更正。
我不得不 :
- 将 uses
属性添加到 EditorMapper
:@Mapper(componentModel = "spring", uses = BookMapper.class)
- 在 BookMapper
中添加替代方法,例如 toDTONoEditor
或 toDTOListNoEditor
,其中我忽略 editor
属性。
- 在 EditorMapper
中映射这些替代方法
- 每个循环依赖都相同
解决方案如下:
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;
}
类别DTO
public class CategoryDTO {
private Long id;
private String category;
private List< BookDTO > bookList;
}
BookMapper
@Mapper(componentModel = "spring", uses = {CategoryMapper.class, EditorMapper.class})
public interface BookMapper {
@Named( "NoBook" )
@Mappings( {
@Mapping(target = "categoryList", qualifiedByName = "NoBook"),
@Mapping( target = "editor.bookList", ignore = true)
} )
BookDTO toDTO( BookEntity bookEntity );
@Named( "NoEditor" )
@Mappings( {
@Mapping(target = "editor", ignore = true),
@Mapping(target = "categoryList", qualifiedByName = "NoBook")
} )
BookDTO toDTONoEditor( BookEntity bookEntity );
@Named( "NoCategory" )
@Mappings( {
@Mapping(target = "categoryList", ignore = true),
@Mapping(target = "editor", qualifiedByName = "NoBook")
} )
BookDTO toDTONoCategory( BookEntity bookEntity );
@Named( "NoBook" )
@IterableMapping(qualifiedByName="NoBook")
List<BookDTO> toDTOList( List<BookEntity> bookEntityList );
@Named( "NoEditor" )
@IterableMapping(qualifiedByName="NoEditor")
List<BookDTO> toDTOListNoEditor( List<BookEntity> bookEntityList );
@Named( "NoCategory" )
@IterableMapping(qualifiedByName="NoCategory")
List<BookDTO> toDTOListNoCategory( 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( "NoBook" )
@Mapping(target = "bookList", ignore = true)
EditorDTO toDTONoBook( EditorEntity editorEntity );
@Named( "NoEditor" )
@IterableMapping(qualifiedByName="NoEditor")
List< EditorDTO > toDTOList( List< EditorEntity > editorEntityList );
@Named( "NoBook" )
@IterableMapping(qualifiedByName="NoBook")
List< EditorDTO > toDTOListNoBook( List< EditorEntity > editorEntityList );
@Named( "NoBook" )
@Mapping(target = "bookList", qualifiedByName = "NoBook")
EditorEntity toEntity( EditorDTO editorDTO );
@Named( "NoBook" )
@IterableMapping(qualifiedByName="NoBook")
List< EditorEntity > toEntityList( List< EditorDTO > editorDTOList );
}
类别映射器
@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 );
@Named( "NoCategory" )
@IterableMapping(qualifiedByName="NoCategory")
List<CategoryEntity> toEntityList( List< CategoryDTO > categoryDTOList );
@Named( "NoBook" )
@IterableMapping(qualifiedByName="NoBook")
List<CategoryEntity> toEntityListNoBook( List< CategoryDTO > categoryDTOList );
}
这样,循环依赖在进入无限循环之前就被打破了。
然而,它的满意度达到 99%,因为 Editor
和 Book
对象并不完全干净。 Editor
包含 bookList。但 bookList
中的每本书仍然包含一个空的 editor
字段。对于 Book
对象反之亦然。
但这似乎是一个反/序列化问题,而不是 MapStruct 问题。
这是 Json 结果
编辑
{
"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
}
]
}
预订
{
"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
}
}
类别
{
"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
}
]
}
希望这有帮助:)
关于java mapstruct 1.3.1 忽略双向 DTO 映射列表中的属性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60615525/