最后
各位读者,由于本篇幅度过长,为了避免影响阅读体验,下面我就大概概括了整理了
▐ 更新Bean实例
有时我们并不一定创建一个新的Bean,可能需要更新某一个实例。这种类型的映射我们可以通过在参数上增加一个@MappingTarget注解。例子如下:
@Mapper
public interface CarMapper {
void updateCarFromDto(CarDto carDto, @MappingTarget Car car);
}
这个例子会把CarDto中的属性值更新的Car对象实例上。上面的例子我们也可以将void改成Car类型返回值。
对于Collection或者Map类型,默认会将集合中所有的值清空,然后使用相关source集合中的值来填充,即CollectionMappingStrategy.ACCESSOR_ONLY策略。另外也提供了CollectionMappingStrategy.ADDER_PREFERRED 或者 CollectionMappingStrategy.TARGET_IMMUTABLE。这些策略可以在@Mapper(collectionMappingStrategy=CollectionMappingStrategy.TARGET_IMMUTABLE)来指定。
▐ 集合映射
基本的定义方式和普通的bean没什么区别,简单例子如下
@Mapper
public interface CarMapper {
Set integerSetToStringSet(Set integers);
List carsToCarDtos(List cars);
CarDto carToCarDto(Car car);
}
对应的生成方法如下
//GENERATED CODE
@Override
public Set integerSetToStringSet(Set integers) {
if ( integers == null ) {
return null;
}
Set set = new HashSet();
for ( Integer integer : integers ) {
set.add( String.valueOf( integer ) );
}
return set;
}
@Override
public List carsToCarDtos(List cars) {
if ( cars == null ) {
return null;
}
List list = new ArrayList();
for ( Car car : cars ) {
list.add( carToCarDto( car ) );
}
return list;
}
对于Map的映射,还提供了@MapMapping
注解,用于处理value的转换
具体的例子如下
public interface SourceTargetMapper {
@MapMapping(valueDateFormat = “dd.MM.yyyy”)
Map<String, String> longDateMapToStringStringMap(Map<Long, Date> source);
}
生成的代码如下
//GENERATED CODE
@Override
public Map<Long, Date> stringStringMapToLongDateMap(Map<String, String> source) {
if ( source == null ) {
return null;
}
Map<Long, Date> map = new HashMap<Long, Date>();
for ( Map.Entry<String, String> entry : source.entrySet() ) {
Long key = Long.parseLong( entry.getKey() );
Date value;
try {
value = new SimpleDateFormat( “dd.MM.yyyy” ).parse( entry.getValue() );
}
catch( ParseException e ) {
throw new RuntimeException( e );
}
map.put( key, value );
}
return map;
}
通过@Mapping#collectionMappingStrategy设置集合的映射策略:CollectionMappingStrategy.ACCESSOR_ONLY:默认、CollectionMappingStrategy.SETTER_PREFERRED、CollectionMappingStrategy.ADDER_PREFERRED、CollectionMappingStrategy.TARGET_IMMUTABLE。
策略具体的意义如果没有看懂,可以参考下这篇文章MapStruct文档(五)——集合映射
▐ 枚举映射处理
直接上例子,方便理解
@Mapper
public interface OrderMapper {
OrderMapper INSTANCE = Mappers.getMapper( OrderMapper.class );
@ValueMappings({
@ValueMapping(source = “EXTRA”, target = “SPECIAL”),
@ValueMapping(source = “STANDARD”, target = “DEFAULT”),
@ValueMapping(source = “NORMAL”, target = “DEFAULT”)
})
ExternalOrderType orderTypeToExternalOrderType(OrderType orderType);
}
生成的代码如下
// GENERATED CODE
public class OrderMapperImpl implements OrderMapper {
@Override
public ExternalOrderType orderTypeToExternalOrderType(OrderType orderType) {
if ( orderType == null ) {
return null;
}
ExternalOrderType externalOrderType_;
switch ( orderType ) {
case EXTRA: externalOrderType_ = ExternalOrderType.SPECIAL;
break;
case STANDARD: externalOrderType_ = ExternalOrderType.DEFAULT;
break;
case NORMAL: externalOrderType_ = ExternalOrderType.DEFAULT;
break;
case RETAIL: externalOrderType_ = ExternalOrderType.RETAIL;
break;
case B2B: externalOrderType_ = ExternalOrderType.B2B;
break;
default: throw new IllegalArgumentException( "Unexpected enum constant: " + orderType );
}
return externalOrderType_;
}
}
默认情况下,如果存在不匹配的情形,则直接抛出异常。这种默认行为是可以被修改的,主要有以下三种策略
-
MappingConstants.NULL : 处理null值,
-
MappingConstants.ANY_REMAINING : 处理所有未被定义或者名字匹配不上的
-
MappingConstants.ANY_UNMAPPED :处理任何违背匹配的情形
枚举到字符串的映射,不支持MappingConstants.ANY_REMAINING
@Mapper
public interface TestMapper {
@ValueMappings({
@ValueMapping(source = “able_status”, target = “PERFECT”),
@ValueMapping(source = MappingConstants.NULL, target = “PASS”),
@ValueMapping(source = “failed_status”, target = MappingConstants.NULL),
@ValueMapping(source = MappingConstants.ANY_UNMAPPED, target = “normal”),
})
String toEnum(DisableStatus disableStatus);
}
@Component
public class TestMapperImpl implements TestMapper {
@Override
public String toEnum(DisableStatus disableStatus) {
if ( disableStatus == null ) {
return “PASS”;
}
String string;
switch ( disableStatus ) {
case able_status: string = “PERFECT”;
break;
case failed_status: string = null;
break;
default: string = “normal”;
}
return string;
}
}
字符串到枚举的映射
@Mapper
public interface TestMapper {
@ValueMappings({
@ValueMapping(source = “PERFECT”, target = “able_status”),
@ValueMapping(source = “PASS”, target = MappingConstants.NULL),
@ValueMapping(source = MappingConstants.NULL, target = “failed_status”),
@ValueMapping(source = MappingConstants.ANY_UNMAPPED, target = “normal_status”),
})
DisableStatus toEnum(String disableStatus);
}
@Component
public class TestMapperImpl implements TestMapper {
@Override
public DisableStatus toEnum(String disableStatus) {
if ( disableStatus == null ) {
return DisableStatus.failed_status;
}
DisableStatus disableStatus1;
switch ( disableStatus ) {
case “PERFECT”: disableStatus1 = DisableStatus.able_status;
break;
case “PASS”: disableStatus1 = null;
break;
default: disableStatus1 = DisableStatus.normal_status;
}
return disableStatus1;
}
}
@Mapper
public interface TestMapper {
@ValueMappings({
@ValueMapping(source = “PERFECT”, target = “able_status”),
@ValueMapping(source = “PASS”, target = MappingConstants.NULL),
@ValueMapping(source = MappingConstants.NULL, target = “failed_status”),
@ValueMapping(source = MappingConstants.ANY_REMAINING, target = “normal_status”),
})
DisableStatus toEnum(String disableStatus);
}
@Component
public class TestMapperImpl implements TestMapper {
@Override
public DisableStatus toEnum(String disableStatus) {
if ( disableStatus == null ) {
return DisableStatus.failed_status;
}
DisableStatus disableStatus1;
switch ( disableStatus ) {
case “PERFECT”: disableStatus1 = DisableStatus.able_status;
break;
case “PASS”: disableStatus1 = null;
break;
case “able_status”: disableStatus1 = DisableStatus.able_status;
break;
case “disable_status”: disableStatus1 = DisableStatus.disable_status;
break;
case “normal_status”: disableStatus1 = DisableStatus.normal_status;
break;
case “failed_status”: disableStatus1 = DisableStatus.failed_status;
break;
case “ok_status”: disableStatus1 = DisableStatus.ok_status;
break;
case “fine_status”: disableStatus1 = DisableStatus.fine_status;
break;
default: disableStatus1 = DisableStatus.normal_status;
}
return disableStatus1;
}
}
- 自定义名称转换
可以通过删除或添加源枚举字符串的前后缀来映射目标枚举对象。
public enum LevelEnum {
able(1, “完美”),
disable(2, “合格”),
normal(3, “普通”),
failed(4, “不及格”),
ok(5, “还行”),
fine(6, “可以”);
private Integer code;
private String desc;
LevelEnum(Integer code, String desc) {
this.code = code;
this.desc = desc;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
}
public enum DisableStatus {
able_status(1, “完美”),
disable_status(2, “合格”),
normal_status(3, “普通”),
failed_status(4, “不及格”),
ok_status(5, “还行”),
fine_status(6, “可以”);
private Integer code;
private String desc;
DisableStatus(Integer code, String desc) {
this.code = code;
this.desc = desc;
}
}
@Mapper
public interface TestMapper {
@EnumMapping(nameTransformationStrategy = “stripSuffix”, configuration = “_status”)
LevelEnum toEnum(DisableStatus disableStatus);
}
@Component
public class TestMapperImpl implements TestMapper {
@Override
public LevelEnum toEnum(DisableStatus disableStatus) {
if ( disableStatus == null ) {
return null;
}
LevelEnum levelEnum;
switch ( disableStatus ) {
case able_status: levelEnum = LevelEnum.able;
break;
case disable_status: levelEnum = LevelEnum.disable;
break;
case normal_status: levelEnum = LevelEnum.normal;
break;
case failed_status: levelEnum = LevelEnum.failed;
break;
case ok_status: levelEnum = LevelEnum.ok;
break;
case fine_status: levelEnum = LevelEnum.fine;
break;
default: throw new IllegalArgumentException( "Unexpected enum constant: " + disableStatus );
}
return levelEnum;
}
}
@EnumMapping#nameTransformationStrategy支持的参数有:suffix(添加源后缀)、stripSuffix(删除源后缀)、prefix(添加源前缀)、stripPrefix(删除源前缀)。
检索映射器
前面已经了解如何自定义对象转换器,接下来看看如何使用已经定义好的对象转换器。
▐ 非依赖注入的方式
当我们不使用DI框架,Mapper实例可以通过org.mapstruct.factory.Mappers
。只需要调用getMapper方法,传递接口类型的mapper就可以获得MapStruct自动生成的Mapper
像前面的例子,我们可以定义INSTANCE属性用于调用方法。例如
@Mapper(componentModel = “default”)
public interface CarMapper {
CarMapper INSTANCE = Mappers.getMapper( CarMapper.class );
CarDto carToCarDto(Car car);
}
通过MapStruct自动生成的mapper是无状态的和线程安全的,可以同时被若干个线程访问。
▐ 使用依赖注入
如果项目使用了依赖注入框架,比如spring。可以使用依赖注入的方式获取映射器。
定义的方式如下:
@Mapper(componentModel = “spring”)
public interface CarMapper {
CarDto carToCarDto(Car car);
}
使用的方式和普通的spring bean一样,
@AutoWired
private CarMapper mapper;
▐ 注入策略
当使用DI注入策略模式时,可以选择field和constructor俩种注入方式。这个可以被@Mapper或者@MapperConfig注解来指定。
使用constructor注入的例子如下:
@Mapper(componentModel = “spring”, uses = EngineMapper.class, injectionStrategy = InjectionStrategy.CONSTRUCTOR)
public interface CarMapper {
CarDto carToCarDto(Car car);
}
生成的映射器将注入uses属性中定义的所有类。当使用InjectionStrategy#CONSTRUCTOR
,构造函数将具有适当的注解,而字段则没有。当使用InjectionStrategy#FIELD
,注解字段位于field本身。目前,默认的注入策略是field注入。建议使用构造函数注入来简化测试。
▐ 检索总结
检索映射器主要有以下几种,支持的值包括:
-
default:通过Mapper#getMapper(class)来获取实例
-
cdi:生成的映射器是一个应用程序范围的CDI bean,可以通过@Inject进行检索
架构学习资料
由于篇幅限制小编,pdf文档的详解资料太全面,细节内容实在太多啦,所以只把部分知识点截图出来粗略的介绍,每个小节点里面都有更细化的内容!
入方式。这个可以被@Mapper或者@MapperConfig注解来指定。
使用constructor注入的例子如下:
@Mapper(componentModel = “spring”, uses = EngineMapper.class, injectionStrategy = InjectionStrategy.CONSTRUCTOR)
public interface CarMapper {
CarDto carToCarDto(Car car);
}
生成的映射器将注入uses属性中定义的所有类。当使用InjectionStrategy#CONSTRUCTOR
,构造函数将具有适当的注解,而字段则没有。当使用InjectionStrategy#FIELD
,注解字段位于field本身。目前,默认的注入策略是field注入。建议使用构造函数注入来简化测试。
▐ 检索总结
检索映射器主要有以下几种,支持的值包括:
-
default:通过Mapper#getMapper(class)来获取实例
-
cdi:生成的映射器是一个应用程序范围的CDI bean,可以通过@Inject进行检索
架构学习资料
[外链图片转存中…(img-ZyCZPGhh-1715589778809)]
[外链图片转存中…(img-uNwF1Y07-1715589778809)]
[外链图片转存中…(img-3FZJc5or-1715589778809)]
[外链图片转存中…(img-d2GFQz0C-1715589778809)]
[外链图片转存中…(img-j7GBwWVB-1715589778810)]
由于篇幅限制小编,pdf文档的详解资料太全面,细节内容实在太多啦,所以只把部分知识点截图出来粗略的介绍,每个小节点里面都有更细化的内容!