MapStruct 映射框架
使用方式
实例方式
@Mapper
public interface StorageMapper {
StorageMapper INSTANCE = Mappers.getMapper( StorageMapper.class );
@ToEntity
@Mapping( target = "weightLimit", source = "maxWeight")
ShelveEntity map(ShelveDto source);
@ToEntity
@Mapping( target = "label", source = "designation")
BoxEntity map(BoxDto source);
}**
CarMapper mapper = Mappers.getMapper( CarMapper.class );
使用StorageMapper.INSTANCE.map方法即可。
注入spring方式
@Mapper(componentModel = "spring", imports = {StringUtils.class, ObjectUtils.class}, nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
public interface SystemMapper {
SysUserRsp toSysUserRsp(SysUserEntity entity);
}
使用@Resource注入即可。
imports 导入引用类。
nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE 表示忽略空值
@Mapper使用场景
源字段和目标字段名不一致场景
@Mapper
public interface CarMapper {
@Mapping(target = "manufacturer", source = "make")
@Mapping(target = "seatCount", source = "numberOfSeats")
CarDto carToCarDto(Car car);
@Mapping(target = "fullName", source = "name")
PersonDto personToPersonDto(Person person);
}
字段一致时可以不填写。
具有多个源参数的映射方法场景
@Mapper
public interface AddressMapper {
@Mapping(target = "description", source = "person.description")
@Mapping(target = "houseNumber", source = "address.houseNo")
DeliveryAddressDto personAndAddressToDeliveryAddressDto(Person person, Address address);
}
直接引用场景时
@Mapper
public interface AddressMapper {
@Mapping(target = "description", source = "person.description")
@Mapping(target = "houseNumber", source = "hn")
DeliveryAddressDto personAndAddressToDeliveryAddressDto(Person person, Integer hn);
}
表达式场景
使用表达式的映射方法
@Mapper
public interface SourceTargetMapper {
SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class );
@Mapping(target = "timeAndFormat",
expression = "java( new org.sample.TimeAndFormat( s.getTime(), s.getFormat() ) )")
Target sourceToTarget(Source s);
}
```‘
声明导入
```java
imports org.sample.TimeAndFormat;
@Mapper( imports = TimeAndFormat.class )
public interface SourceTargetMapper {
SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class );
@Mapping(target = "timeAndFormat",
expression = "java( new TimeAndFormat( s.getTime(), s.getFormat() ) )")
Target sourceToTarget(Source s);
}
默认表达式
默认表达式是默认值和表达式的组合。只有当源属性为null。
适用于表达式的默认表达式的警告和限制相同。仅支持Java,并且MapStruct不会在生成时验证表达式。
下面的示例演示了当源属性不存在时,如何使用默认表达式设置值 (例如,是null):
imports java.util.UUID;
@Mapper( imports = UUID.class )
public interface SourceTargetMapper {
SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class );
@Mapping(target="id", source="sourceId", defaultExpression = "java( UUID.randomUUID().toString() )")
Target sourceToTarget(Source s);
}
条件映射场景
Mapper使用自定义条件检查方法
@Mapper
public interface CarMapper {
CarDto carToCarDto(Car car);
@Condition
default boolean isNotEmpty(String value) {
return value != null && !value.isEmpty();
}
}
// GENERATED CODE
public class CarMapperImpl implements CarMapper {
@Override
public CarDto carToCarDto(Car car) {
if ( car == null ) {
return null;
}
CarDto carDto = new CarDto();
if ( isNotEmpty( car.getOwner() ) ) {
carDto.setOwner( car.getOwner() );
}
// Mapping of other properties
return carDto;
}
}
条件映射是一种源存在检查。 不同之处在于,它允许用户编写自定义条件方法,这些方法将被调用以检查是否需要映射属性。
自定义条件方法是用org.mapstruct.Condition并返回boolean。
例如,如果你只想映射一个字符串属性,当它不是 “null”,它不是空的,那么你可以做这样的事情:
更新现有bean实例场景
在这里插入代码片
@Mapper
public interface CarMapper {
void updateCarFromDto(CarDto carDto, @MappingTarget Car car);
}
@MappingTarget Car car 指定更新的目标对象。
数据类型转换场景
从int到String的转换 numberFormat
@Mapper
public interface CarMapper {
@Mapping(source = "price", numberFormat = "$#.00")
CarDto carToCarDto(Car car);
@IterableMapping(numberFormat = "$#.00")
List<String> prices(List<Integer> prices);
}
从日期转换为字符串 dateFormat
@Mapper
public interface CarMapper {
@Mapping(source = "manufacturingDate", dateFormat = "dd.MM.yyyy")
CarDto carToCarDto(Car car);
@IterableMapping(dateFormat = "dd.MM.yyyy")
List<String> stringListToDateList(List<Date> dates);
}
映射器控制嵌套bean映射II
@Mapper
public interface FishTankMapperWithDocument {
@Mapping(target = "fish.kind", source = "fish.type")
@Mapping(target = "fish.name", expression = "java(\"Jaws\")")
@Mapping(target = "plant", ignore = true )
@Mapping(target = "ornament", ignore = true )
@Mapping(target = "material", ignore = true)
@Mapping(target = "quality.document", source = "quality.report")
@Mapping(target = "quality.document.organisation.name", constant = "NoIdeaInc" )
FishTankWithNestedDocumentDto map( FishTank source );
}
常量映射constant = “NoIdeaInc”
定义表达式映射
expression = “java(“Jaws”)”
调用其他映射器场景
手动实现的mapper类
public class DateMapper {
public String asString(Date date) {
return date != null ? new SimpleDateFormat( "yyyy-MM-dd" )
.format( date ) : null;
}
public Date asDate(String date) {
try {
return date != null ? new SimpleDateFormat( "yyyy-MM-dd" )
.parse( date ) : null;
}
catch ( ParseException e ) {
throw new RuntimeException( e );
}
}
}
引用另一个mapper类
@Mapper(uses=DateMapper.class)
public interface CarMapper {
CarDto carToCarDto(Car car);
}
生成实现的代码时carToCarDto()方法,MapStruct将查找一个映射Date对象转换为字符串,则在DateMapper类并生成的调用asString()用于映射manufacturingDate属性。
使用defaultValue的映射器场景
使用defaultValue的映射器
@Mapper
public interface MovieMapper {
@Mapping( target = "category", qualifiedByName = "CategoryToString", defaultValue = "DEFAULT" )
GermanRelease toGerman( OriginalRelease movies );
@Named("CategoryToString")
default String defaultValueForQualifier(Category cat) {
// some mapping logic
}
}
在上面的示例中,如果类别为null,则方法CategoryToString( Enum.valueOf( Category.class, “DEFAULT” ) )将被调用,结果将被设置为类别字段。
使用defaultValue和default方法的映射器。
@Mapper
public interface MovieMapper {
@Mapping( target = "category", qualifiedByName = "CategoryToString", defaultValue = "Unknown" )
GermanRelease toGerman( OriginalRelease movies );
@Named("CategoryToString")
default String defaultValueForQualifier(Category cat) {
// some mapping logic
}
@Named("CategoryToString")
default String defaultValueForQualifier(String value) {
return value;
}
}
映射器使用defaultExpression
@Mapper
public interface MovieMapper {
@Mapping( target = "category", qualifiedByName = "CategoryToString", defaultExpression = "java(\"Unknown\")" )
GermanRelease toGerman( OriginalRelease movies );
@Named("CategoryToString")
default String defaultValueForQualifier(Category cat) {
// some mapping logic
}
}
映射集合场景(list,map,set)
具有集合映射方法的Mapper
@Mapper
public interface CarMapper {
Set<String> integerSetToStringSet(Set<Integer> integers);
List<CarDto> carsToCarDtos(List<Car> cars);
CarDto carToCarDto(Car car);
}
注意,映射 List集合时,要写carToCarDto映射规则,不然可能报错。
当映射bean的集合类型属性时,MapStruct将查找具有匹配参数和返回类型的集合映射方法,例如Car#passengers(类型List) 至CarDto#passengers(类型List)。
使用集合映射方法映射bean属性
//GENERATED CODE
carDto.setPassengers( personsToPersonDtos( car.getPassengers() ) );
...
map集合场景
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 LinkedHashMap<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;
}
Stream流场景
@Mapper
public interface CarMapper {
Set<String> integerStreamToStringSet(Stream<Integer> integers);
List<CarDto> carsToCarDtos(Stream<Car> cars);
CarDto carToCarDto(Car car);
}
//GENERATED CODE
@Override
public Set<String> integerStreamToStringSet(Stream<Integer> integers) {
if ( integers == null ) {
return null;
}
return integers.map( integer -> String.valueOf( integer ) )
.collect( Collectors.toCollection( LinkedHashSet<String>::new ) );
}
@Override
public List<CarDto> carsToCarDtos(Stream<Car> cars) {
if ( cars == null ) {
return null;
}
return cars.map( car -> carToCarDto( car ) )
.collect( Collectors.toCollection( ArrayList<CarDto>::new ) );
}
将枚举映射到枚举类型场景
枚举映射方法
@Mapper
public interface OrderMapper {
OrderMapper INSTANCE = Mappers.getMapper( OrderMapper.class );
@ValueMappings({
@ValueMapping(target = "SPECIAL", source = "EXTRA"),
@ValueMapping(target = "DEFAULT", source = "STANDARD"),
@ValueMapping(target = "DEFAULT", source = "NORMAL")
})
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_;
}
}