【问题标题】:Mapping objects with bi-directional relations with Mapstruct使用 Mapstruct 映射具有双向关系的对象
【发布时间】:2019-04-12 07:51:57
【问题描述】:

我想知道 Mapstruct 是否以及如何帮助映射具有双向关系的对象(在我的例子中是一对多):

public class A{
     private Set<B> listB;
}

public class B{
     private A a;
}

从/到实体的映射会产生StackOverflowError。 (我希望这会发生)。 另一方面,封闭的 Mapstruct 问题 4691163 似乎暗示 mapstruct 不会直接支持它。 我试过这个例子:

https://github.com/mapstruct/mapstruct-examples/tree/master/mapstruct-mapping-with-cycles

但这只是行不通。无论是否应用“CycleAvoidingMappingContext”,stackoverflowerror 都保持不变。

那么如何使用循环映射对象并利用 mapstruct? (我想避免完全手动映射)

【问题讨论】:

  • 你能提供不适合你的代码吗?您链接的示例应该可以工作。
  • 我使用示例中的代码进行了尝试。使用此类:github.com/mapstruct/mapstruct-examples/blob/master/…
  • 也许显示您尝试过的映射器会帮助我们更多地了解错误所在。我刚刚使用您提供的信息进行了尝试(并回答了),它按预期工作。

标签: java stack-overflow bidirectional mapstruct


【解决方案1】:

为了使映射工作,您可以尝试使用以下映射器:

@Mapper
public interface MyMapper {

    A map(A a, @Context CycleAvoidingMappingContext context);

    Set<B> map(Set<B> b, @Context CycleAvoidingMappingContext context);

    B map(B b, @Context CycleAvoidingMappingContext context);
}

如果没有从Set&lt;B&gt; 映射到Set&lt;B&gt; 的方法,则不会映射A 中的集合。

【讨论】:

    【解决方案2】:

    与此同时,我找到了一个解决方案:首先我忽略了相关字段并将其映射到“AfterMapping”中。就我而言,我也想独立映射孩子。在我的情况下,@Context 只包含一个Booleanboolean 不起作用),它告诉我进入循环的位置(startedFromPrupru 是父级):

    父映射器(包含一组许可证):

    @Mapper(componentModel = "spring", uses = {DateTimeMapper.class, LicenseMapper.class, CompanyCodeMapper.class, ProductHierarchyMapper.class})
    public abstract class ProductResponsibleUnitMapper {
    
        @Autowired
        private LicenseMapper licenseMapper;
    
        @Mappings({@Mapping(target = "licenses", ignore = true)})
        public abstract ProductResponsibleUnit fromEntity(ProductResponsibleUnitEntity entity, @Context Boolean startedFromPru);
    
        @Mappings({@Mapping(target = "licenses", ignore = true)})
        public abstract ProductResponsibleUnitEntity toEntity(ProductResponsibleUnit domain, @Context Boolean startedFromPru);
    
        @AfterMapping
        protected void mapLicenseEntities(ProductResponsibleUnit pru, @MappingTarget ProductResponsibleUnitEntity pruEntity, @Context Boolean startedFromPru){
            if(startedFromPru) {
                pruEntity.getLicenses().clear(); //probably not necessary
                pru.getLicenses().stream().map(license -> licenseMapper.toEntity(license, startedFromPru)).forEach(pruEntity::addLicense);
            }
        }
    
        @AfterMapping
        protected void mapLicenseEntities(ProductResponsibleUnitEntity pruEntity, @MappingTarget ProductResponsibleUnit pru, @Context Boolean startedFromPru){
            if(startedFromPru) {
                pru.getLicenses().clear(); //probably not necessary
                pruEntity.getLicenses().stream().map(licenseEntity -> licenseMapper.fromEntity(licenseEntity, startedFromPru)).forEach(pru::addLicense);
            }
        }
    
    }
    

    子映射器:

    @Mapper(componentModel = "spring", uses = { DateTimeMapper.class, CompanyCodeMapper.class, ProductHierarchyMapper.class,
            CountryCodeMapper.class })
    public abstract class LicenseMapper {
    
        @Autowired
        private ProductResponsibleUnitMapper pruMapper;
    
        @Mappings({ @Mapping(source = "licenseeId", target = "licensee"), @Mapping(target = "licensor", ignore = true) })
        public abstract License fromEntity(LicenseEntity entity, @Context Boolean startedFromPru);
    
    
        @Mappings({ @Mapping(source = "licensee", target = "licenseeId"), @Mapping(target = "licensor", ignore = true) })
        public abstract LicenseEntity toEntity(License domain, @Context Boolean startedFromPru);
    
        @AfterMapping
        protected void mapPru(LicenseEntity licenseEntity, @MappingTarget License license,
                @Context Boolean startedFromPru) {
            //only call ProductResponsibleUnitMapper if mapping did not start from PRU -> startedFromPru is false
            if (!startedFromPru) {
                license.setLicensor(pruMapper.fromEntity(licenseEntity.getLicensor(), startedFromPru));
            }
        }
    
        @AfterMapping
        protected void mapPru(License license, @MappingTarget LicenseEntity licenseEntity,
                @Context Boolean startedFromPru) {
            //only call ProductResponsibleUnitMapper if mapping did not start from PRU -> startedFromPru is false
            if (!startedFromPru) {
                licenseEntity.setLicensor(pruMapper.toEntity(license.getLicensor(), startedFromPru));
            }
        }
    

    【讨论】:

      猜你喜欢
      • 2020-05-10
      • 2015-08-19
      • 1970-01-01
      • 2016-10-14
      • 1970-01-01
      • 2018-04-02
      • 2020-03-27
      • 1970-01-01
      • 2021-02-17
      相关资源
      最近更新 更多