今天我开始使用 MapStruct 为我的项目创建我的模型到 DTO 转换器,我想知道它是否自动处理循环引用但事实证明它没有。
这是我用来测试它的转换器:
package it.cdc.snp.services.rest.giudizio;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;
import org.springframework.stereotype.Component;
import it.cdc.snp.dto.entita.Avvisinotifica;
import it.cdc.snp.dto.entita.Corrispondenza;
import it.cdc.snp.model.notifica.AvvisoDiNotificaModel;
import it.cdc.snp.model.notifica.NotificaModel;
import it.cdc.snp.model.procedimento.ProcedimentoModel;
@Component
@Mapper(componentModel="spring")
public interface NotificaMapper {
NotificaMapper INSTANCE = Mappers.getMapper( NotificaMapper.class );
@Mappings({
@Mapping(source = "avvisinotificas", target = "avvisinotificas"),
})
NotificaModel<ProcedimentoModel> corrispondenzaToNotificaModel(Corrispondenza notifica);
@Mappings({
@Mapping(source = "corrispondenza", target = "notifica"),
})
AvvisoDiNotificaModel avvisinotificaToAvvisoDiNotificaModel(Avvisinotifica avvisinotifica);
}
这是测试:
Notifica sourceObject1 = new Notifica();
sourceObject1.setId(new Long(1));
Avvisinotifica sourceObject2 = new Avvisinotifica();
sourceObject2.setId(new Long(11));
List<Avvisinotifica> tests= new ArrayList<>();
tests.add(sourceObject2);
sourceObject1.setAvvisinotificas(tests);
sourceObject2.setCorrispondenza(sourceObject1);
NotificaModel destObject1 = new NotificaModel<>();
Avvisinotifica destObject2 = new Avvisinotifica();
NotificaModel converted = mapper.corrispondenzaToNotificaModel(sourceObject1);
Notifica、Avvisinotifica 和它们各自的模型是带有 setter 和 getter 的简单 POJO,所以我认为不需要发布代码(Notifica 扩展了 Corrispondenza,如果您想知道的话)
这段代码进入了一个无限循环,这里没有什么特别令人惊讶的(尽管我希望它能处理这些情况)。
虽然我认为我可以找到一种优雅的方法来手动处理它(我正在考虑使用带有 @MappingTarget
的方法来插入引用的对象),但我想知道是否有某种方法可以告诉 MapStruct 如何自动处理循环引用。
最佳答案
Notifica 和 Avvisinotifica 无法帮助我理解您的模型。因此,假设您有上述子模型和父模型,
public class Child {
private int id;
private Father father;
// Empty constructor and getter/setter methods omitted.
}
public class Father {
private int x;
private List<Child> children;
// Empty constructor and getter/setter methods omitted.
}
public class ChildDto {
private int id;
private FatherDto father;
// Empty constructor and getter/setter methods omitted.
}
public class FatherDto {
private int id;
private List<ChildDto> children;
// Empty constructor and getter/setter methods omitted.
}
你应该像这样创建一个映射器,
@Mapper
public abstract class ChildMapper {
@AfterMapping
protected void ignoreFathersChildren(Child child, @MappingTarget ChildDto childDto) {
childDto.getFather().setChildren(null);
}
public abstract ChildDto myMethod(Child child);
}
Mapstuct的初始版本
最好按照下面的方法。此解决方案假定 ChildDto::father 属性是 Father 类型,而不是 FatherDto,这不是正确的数据架构。
@AfterMapping注释意味着在属性映射之后,该方法将被导入到生成的源代码中。因此,Mapper 实现将是这样的,
@Component
public class ChildMapperImpl extends ChildMapper {
@Override
public ChildDto myMethod(Child child) {
if ( child == null ) {
return null;
}
ChildDto childDto = new ChildDto();
childDto.setId( child.getId() );
childDto.setFather( child.getFather() );
ignoreFathersChildren( child, childDto );
return childDto;
}
}
在此实现中,子项具有父项集。这意味着存在循环引用,但使用 ignoreFathersChildren(child, childDto)
方法我们删除了引用(我们将其设置为 null)。
更新1
使用 mapstruct 版本 1.2.0.Final 你可以做得更好,
@Mapper
public interface ChildMapper {
@Mappings({
// @Mapping(target = "father", expression = "java(null)"),
@Mapping(target = "father", qualifiedByName = "fatherToFatherDto")})
ChildDto childToChildDto(Child child);
@Named("fatherToFatherDto")
@Mappings({
@Mapping(target = "children", expression = "java(null)")})
FatherDto fatherToFatherDto(Father father);
}
更新2
使用 mapstruct 版本 1.4.2.Final 你可以做得更好,
@Named("FatherMapper")
@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE)
public interface FatherMapper {
@Named("toDto")
@Mappings
FatherDto toDto(Father father);
@Named("toDtoWithoutChildren")
@Mappings({
@Mapping(target = "children", expression = "java(null)")})
FatherDto toDtoWithoutChildren(Father father);
}
@Named("ChildMapper")
@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE, uses = {FatherMapper.class})
public interface ChildMapper {
@Named("toDto")
@Mappings({
@Mapping(target = "father", qualifiedByName = {"FatherMapper", "toDtoWithoutChildren"})})
ChildDto toDto(Child child);
@Named("toDtoWithoutFather")
@Mappings({
@Mapping(target = "father", expression = "java(null)")})
ChildDto toDtoWithoutFather(Child child);
}
关于java - 使用 MapStruct 转换时防止循环引用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36223752/