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注解
您想用抽象 class 替换您的界面,然后
@AfterMapping
void customMapping(@MappingTarget Target target, Source source) {
// any custom logic
}
您声称在表达式中调用外部库不可行。这可能不是真的,具体取决于您调用的库的性质和所使用的框架。
静态方法
如果调用的方法是class上的静态方法,可以直接在@Mapper
的expression
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
使用 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注解
您想用抽象 class 替换您的界面,然后
@AfterMapping
void customMapping(@MappingTarget Target target, Source source) {
// any custom logic
}
您声称在表达式中调用外部库不可行。这可能不是真的,具体取决于您调用的库的性质和所使用的框架。
静态方法
如果调用的方法是class上的静态方法,可以直接在@Mapper
的expression
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