【问题标题】:java mapstruct - mapping a field inside related collectionjava mapstruct - 映射相关集合内的字段
【发布时间】:2019-07-31 19:15:07
【问题描述】:

我想在我的 spring 应用程序中使用 mapstruct 库将模型列表映射到 dto 列表。假设我有两个类似这样的模型:

public class Employee {
    private Integer id;
    private String name;
    private Set<Phone> phones;
}

public class Phone {
    private Integer id;
    private String number;
}

这是我的两个 dto:

public class EmployeeDto {
    private Integer id;
    private String name;
    private Set<PhoneDto> phones;
}

public class PhoneDto {
    private Integer id;
    private String num;
}

最后我在我的映射类中使用了这个方法:

@Mappings({
        @Mapping(target = "num", source = "phones.number")
})
public abstract List<EmployeeDto> toEmployeeDtoList(List<Employee> employeeList);

但是当我想编译时,这会返回java: No property named "phones.number" exists in source parameter(s).。我知道我的代码有问题,但我找不到对我的需要有用的东西。你能帮我解决这个问题吗?

【问题讨论】:

    标签: java mapstruct


    【解决方案1】:

    第一个原因:您应该先指定 object -&gt; object 映射,然后才能指定 collection -&gt; collection 映射(PhoneDto -> Phone, EmployeeDto -> Employee),因为 mapstruct 嵌套表示法不会扩展到集合中。从我的角度来看,您不需要在映射器中保存基本的集合映射。你总能做到:

    employees.stream()
            .map(mapper::toDto)
            .collect(Collectors.toList());
    

    注意: 但是如果您需要在嵌套集合上进行一些特定的collection -&gt; collection 映射,您应该指定它。 (在您的情况下,可能使用下面的 LinkedHashSet 对 Set 进行排序,如果您不指定集合 -> 集合映射,您将失去排序,因为 mapstruct 将使用 HashSet 作为Set&lt;Phones&gt; -&gt; Set&lt;PhonesDto&gt; 转换的默认实现。

    如果映射器可以访问映射,Mapstruct 将选择所有映射链(嵌套的类映射器应该在同一个类中,或者将在@Mapper(uses= 类注释中声明)。


    第二个原因: 你的@Mapping(target = "num", source = "phones.number") phones 集合的哪个元素中检索number。这就像你在尝试写EmployeeDto.num(single record) = Emloyee.phones(multiple records).number(single record)


    恕我直言:使用 mapstruct 的最佳实践是使用干净的接口。这表明您在 entity/dto/view/model/etc 中具有清晰透明的结构和良好的关系。如果需要更具体的东西 - 您始终可以使用 @AfterMapping@BeforeMapping 注释指定默认方法。或者去抽象类实现/装饰器(@DecoratedWith 映射)。

    对于这种情况有一些肮脏的技巧 - @Mapping(target = "num", expression = "java(your_java_code_as_string_in_here)") 但请注意:该表达式是一个字符串,并且会失败仅适用于创建映射器,不适用于所有类型的重构。


    这是您的模型的示例映射(两种方式):

    @Mapper
    public interface EmployeeMapper {
    
      Employee toEmployee(EmployeeDto employeeDto);
    
      EmployeeDto toEmployeeDto(Employee employee);
    
      @Mapping(target="number", source="num")
      Phone toPhone(PhoneDto phoneDto);
    
      @InheritInverseConfiguration
      PhoneDto toPhoneDto(Phone phone);
    
      List<EmployeeDto> toEmployeeDtoList(List<Employee> employeeList);
    }
    

    也是一个值得考虑的好习惯 - 每个逻辑对象对使用不同的映射器。

    @Mapper(uses = {PhoneMapper.class, OtherMapper.class}) // this is class level annotation.
    

    这里收集了很好的例子:https://github.com/mapstruct/mapstruct-examples/

    【讨论】:

    • 你的答案应该是正确的。但是,我不认为 mapstruct 表达了对接口或抽象类的偏好。尽管您的组件模型可能会这样做。问题中的问题是 mapstruct 嵌套表示法没有扩展到集合中。所以我猜想mapstruct可以为元素映射选择的问题的列表映射以及toPhoneDto和@Mapping(target="num",source="number")是必不可少的部分。其余的可能会有所帮助,但不能解决问题
    • @Sjaak,谢谢。我会尝试更准确地编辑我的答案。
    • 没问题..谢谢我这里只有手机,编辑提案很麻烦
    【解决方案2】:

    我在这里找到了您的答案https://www.baeldung.com/mapstruct

    我相信您的问题是您实际上并不希望将值phones.number 映射到num。您希望 Phone 类中的值 number 映射到 PhoneDto 类中的值 num。

    @Mappings({
            @Mapping(target = "num", source = "number")
    })
    public abstract List<EmployeeDto> toEmployeeDtoList(List<Employee> employeeList);
    

    【讨论】:

    • 我尝试了您的回复,但没有成功并得到同样的错误。
    猜你喜欢
    • 2018-03-30
    • 2021-10-09
    • 2017-08-04
    • 2018-10-05
    • 1970-01-01
    • 2016-04-19
    • 2021-09-24
    • 1970-01-01
    • 2021-12-18
    相关资源
    最近更新 更多