Mapstruct:如何将两个字段合并为一个

Mapstruct: How to merge two fields into one

使用 MapStruct 框架,如何将多个字段映射到一个字段(基于自定义逻辑),同时仍将其他字段一对一映射?

这里有一个简单的例子来说明我的意思:

public class Source {
    private String firstname;               
    private String surname;  
    // other fields eg:
    private String address; 
    private int age;
    private int favoriteNumber;
}
public class Target {
    private String fullname; // Sould be firstname + surname
    // other fields eg:
    private String address; 
    private int age;
    private int favoriteNumber;
}

我知道可以使用表达式:

@Mapping(target = "fullname", expression = "java(el.getFirstname() + el.getSurname())")

但在我的特殊用例中(未在此示例中描述),我需要为两个字段的 merging/mapping 使用一些外部库,这对于表达式是不可行的。

有没有办法不用表达式就可以实现合并两个字段?

可以使用@AfterMapping注解

https://mapstruct.org/documentation/stable/reference/html/#customizing-mappings-with-before-and-after

您想用抽象 class 替换您的界面,然后

    @AfterMapping
    void customMapping(@MappingTarget Target target, Source source) {
        // any custom logic
    }

您声称在表达式中调用外部库不可行。这可能不是真的,具体取决于您调用的库的性质和所使用的框架。

静态方法

如果调用的方法是class上的静态方法,可以直接在@Mapperexpression annotation element of @Mapping. To avoid having to fully qualify the class being called, the imports元素内调用。

@Mapper(imports = ExternalLibrary.class)
public interface SourceToTargetMapper {
    @Mapping(target = "fullname",
            expression = "java(ExternalLibrary.toFullName(s.getFirstname(), s.getSurname()))")
    Target map(Source s);
}

Spring 框架 bean

如果库方法是 Spring bean 上的方法,则可以使用 Spring 组件模型将映射器制成 bean,并且包含库的 Spring bean方法可以注入映射器:

@Mapper(componentModel = "spring")
public static abstract class SourceToTargetMapper {
    @Autowired
    ExternalLibrary externalLibrary;

    @Mapping(target = "fullname",
            expression = "java(externalLibrary.toFullName(s.getFirstname(), s.getSurname()))")
    abstract Target map(Source s);
}

要使用这个映射器,将它作为一个 Spring bean 注入,然后在 bean 上调用映射方法:

@Component
public class Example {
  @Autowired
  private SourceToTargetMapper mapper;

  public void demonstrate(Source s) {
    System.out.println(mapper.map(s));
  }
}

我自己还没有测试过,但我想这种注入方法可以与 MapStruct (cdi & jsr330) 支持的其他 component models 一起使用。

一种方法是将 Source 对象中的 custom mapping method 添加到合并值,然后将合并字段的源声明为整个源对象:

interface CustomMappingMethodMapper {
    @Mapping(target = "fullname", source = ".")
    Target map(Source source);

    default String getFullName(Source s) {
        return s.getFirstname() + " " + s.getSurname();
    }
}

我需要将 2 个基本字段组合到包装对象中。这是我做的 id

 @Mapping(source = "quantity", target = "energyQuantity.quantity")
 @Mapping(source = "quantityUnit", target = "energyQuantity.energyQuantityUnit", qualifiedByName = "tradeSearchEnergyQuantityUnitToEnergyQuantityUnit")
    Trade tradeSearchToDomainDTO(api.client.generated.model.Trade trade);

其中 energyQuantity 是 2 个字段的包装器 class