java mapstruct - 在相关集合中映射字段

问题描述 投票:0回答:3

我想在我的 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
3个回答
6
投票

第一个原因:您应该先指定

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

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

注意:但是如果您需要在嵌套集合上进行一些特定的

collection -> collection
映射,则应该指定它。 (在您的情况下,Set 可能使用下面的 LinkedHashSet 进行排序,如果您不指定集合 -> 集合映射,您将失去排序,因为 Mapstruct 将使用 HashSet 作为
Set<Phones> -> Set<PhonesDto>
转换的默认实现)。

如果映射器可以访问映射,Mapstruct 将选择所有映射链(嵌套类映射器应该位于同一类中,或者在

@Mapper(uses=
类注释中进行说明)。


第二个原因: 您的

@Mapping(target = "num", source = "phones.number")
<<-- won't work because it doesn't know from what element from
phones
收藏,
number
应该被检索。就像您正在尝试写
EmployeeDto.num(single record) = Emloyee.phones(multiple records).number(single record)


恕我直言:使用mapstruct的最佳实践是使用干净的接口。这表明您在实体/dto/视图/模型/等中拥有清晰透明的结构和良好的关系。如果需要更具体的东西 - 您始终可以使用

@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/


0
投票
  • 您的 Phone 类没有映射器。
  • 您的电话号码字段在实体和 dto 之间具有不同的名称
  • 您应该将电话映射器机制应用到您的父接口 Employee

PhoneMapper.class:

@Mapper
public interface PhoneMapper  {
    @Mapping(target = "num", source = "number")
    PhoneDto toDto(Phone phone);
}

EmployeeMapper.class

@Mapper(uses = PhoneMapper.class)
public interface EmployeeMapper {

    List<EmployeeDto> toDto(List<Employee> e);
}

这应该有效


-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);
© www.soinside.com 2019 - 2024. All rights reserved.