MapStruct
是一种类型安全的bean
映射类生成java注释处理器
。
我们要做的就是定义一个映射器接口,声明任何必需的映射方法。在编译的过程中,MapStruct会生成此接口的实现。该实现使用纯java方法调用的源和目标对象之间的映射,MapStruct节省了时间,通过生成代码完成繁琐和容易出错的代码逻辑。。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0http://maven.apache.org/xsd/maven-4.0.0.xsd">
<properties>
// ...
<org.mapstruct.version>1.2.0.Final</org.mapstruct.version>
</properties>
<dependencies>
...
<!-- MapStruct START -->
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-jdk8</artifactId>
<version>${org.mapstruct.version}</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
</dependency>
<!-- MapStruct END -->
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<annotationProcessorPaths>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
</path>
</annotationProcessorPaths>
<compilerArgs>
<compilerArg>-Amapstruct.defaultComponentModel=spring</compilerArg>
<compilerArg>-Amapstruct.suppressGeneratorTimestamp=true</compilerArg>
<compilerArg>-Amapstruct.suppressGeneratorVersionInfoComment=true</compilerArg>
</compilerArgs>
</configuration>
</plugin>
</plugins>
</build>
</project>
public interface BasicObjectMapper<SOURCE, TARGET> {
@Mappings({})
@InheritConfiguration
TARGET to(SOURCE var1);
@InheritConfiguration
List<TARGET> to(List<SOURCE> var1);
@InheritInverseConfiguration
SOURCE from(TARGET var1);
@InheritInverseConfiguration
List<SOURCE> from(List<TARGET> var1);
}
@Data
public class ProductCategory {
/** 类别编码 */
private String categoryCode;
/** 类别名称 */
private String categoryName;
}
@Data
public class CategoryVo {
private String code;
private String name;
}
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;
@Mapper
public interface CategoryMapper extends BasicObjectMapper<CategoryVo, ProductCategory> {
CategoryMapper MAPPER = Mappers.getMapper(CategoryMapper.class);
@Mappings({
@Mapping(source = "code", target = "categoryCode"),
@Mapping(source = "name", target = "categoryName")
})
ProductCategory to(CategoryVo source);
}
public static void main(String[] args) {
CategoryMapper categoryMapper = CategoryMapper.MAPPER;
CategoryVo vo = new CategoryVo();
vo.setCode("0000");
vo.setName("属性名称");
ProductCategory pc = categoryMapper.to(vo); // 通过 to方法得到 ProductCategory
System.out.println("1" + pc);
CategoryVo vo1 = categoryMapper.from(pc); // 通过 from方法得到 CategoryVo,既反转 to方法
System.out.println("2" + vo1);
List<ProductCategory> pcList = categoryMapper.to(Arrays.asList(vo, vo1)); // 通过to方法从集合得到转换后的集合
System.out.println("3" + pcList);
List<CategoryVo> voList = categoryMapper.from(pcList); // 反转集合
System.out.println("4" + voList);
}
@Mapper
public interface CarMapper {
CarMapper MAPPER = Mappers.getMapper(CarMapper.class);
@Mappings({...})
CarDto carToCarDto(Car car);
default PersonDto personToPersonDto(Person person) {
// hand-written mapping logic
}
}
@Mapper
public abstract class CarMapper {
@Mappings(...)
public abstract CarDto carToCarDto(Car car);
public PersonDto personToPersonDto(Person person) {
// hand-written mapping logic
}
}
@Mapper
public interface AddressMapper {
@Mappings({
@Mapping(source = "person.description", target = "description"),
@Mapping(source = "address.houseNo", target = "houseNumber")
})
DeliveryAddressDto personAndAddressToDeliveryAddressDto(Person person, Address address);
}
MapStruct 还提供直接引用源参数
@Mapper
public interface AddressMapper {
@Mappings({
@Mapping(source = "person.description", target = "description"),
@Mapping(source = "hn", target = "houseNumber")
})
DeliveryAddressDto personAndAddressToDeliveryAddressDto(Person person, Integer hn);
}
public class Customer {
private Long id;
private String name;
// getters and setter omitted for brevity
}
public class CustomerDto {
public Long id;
public String customerName;
}
@Mapper
public interface CustomerMapper {
CustomerMapper MAPPER = Mappers.getMapper( CustomerMapper.class );
@Mapping(source = "customerName", target = "name")
Customer toCustomer(CustomerDto customerDto);
@InheritInverseConfiguration
CustomerDto fromCustomer(Customer customer);
}
// 生成的映射器如下
public class CustomerMapperImpl implements CustomerMapper {
@Override
public Customer toCustomer(CustomerDto customerDto) {
// ...
customer.setId( customerDto.id );
customer.setName( customerDto.customerName );
// ...
}
@Override
public CustomerDto fromCustomer(Customer customer) {
// ...
customerDto.id = customer.getId();
customerDto.customerName = customer.getName();
// ...
}
}
@Mapper
public interface CarMapper {
CarMapper INSTANCE = Mappers.getMapper(CarMapper.class);
CarDto carToCarDto(Car car);
}
这种模式使客户非常容易地使用映射器对象,而无需反复实例化新的实例 :
Car car = ...;
CarDto dto = CarMapper.INSTANCE.carToCarDto( car );
使用依赖注入 : 通过 Spring 依赖注入可以获取映射器对象
@Mapper(componentModel = "spring")
public interface CarMapper {
CarDto carToCarDto(Car car);
}
@Inject
private CarMapper mapper;
int 到 String的转换
@Mapper
public interface CarMapper {
@Mapping(source = "price", numberFormat = "$#.00")
CarDto carToCarDto(Car car);
@IterableMapping(numberFormat = "$#.00")
List<String> prices(List<Integer> prices);
}
BigDecimal 转换为 String
@Mapper
public interface CarMapper {
@Mapping(source = "power", numberFormat = "#.##E0")
CarDto carToCarDto(Car car);
}
从日期到字符串的转换
@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);
}
映射对象引用 : 对象中如果包含另一个对象的引用,此时只需为引用的对象类型定义映射方法即可
@Mapper
public interface CarMapper {
CarDto carToCarDto(Car car);
PersonDto personToPersonDto(Person person);
}
# 映射器控制嵌套的bean映射
@Mapper
public interface FishTankMapper {
@Mappings({
@Mapping(target = "fish.kind", source = "fish.type"),
@Mapping(target = "fish.name", ignore = true),
@Mapping(target = "plant", ignore = true ),
@Mapping(target = "ornament", ignore = true ),
@Mapping(target = "material", ignore = true),
@Mapping(target = "ornament", source = "interior.ornament"),
@Mapping(target = "material.materialType", source = "material"),
@Mapping(target = "quality.report.organisation.name", source = "quality.report.organisationName")
})
FishTankDto map( FishTank source );
}
# 手动实现的映射
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(uses = DateMapper.class)
public class CarMapper {
CarDto carToCarDto(Car car);
}
@Mapper
public interface CarMapper {
Set<String> integerSetToStringSet(Set<Integer> integers);
List<CarDto> carsToCarDtos(List<Car> cars);
CarDto carToCarDto(Car car);
}
# 生成的集合映射方法
@Override
public Set<String> integerSetToStringSet(Set<Integer> integers) {
if (integers == null) {
return null;
}
Set<String> set = new HashSet<>();
for (Integer integer : integers) {
set.add(String.valueOf(integer));
}
return set;
}
@Override
public List<CarDto> carsToCarDtos(List<Car> cars) {
if (cars == null) {
return null;
}
List<CarDto> list = new ArrayList<>();
for (Car car : cars) {
list.add(carToCarDto(car));
}
return list;
}
映射Map :
public interface SourceTargetMapper {
@MapMapping(valueDateFormat = "dd.MM.yyyy")
Map<String, String> longDateMapToStringStringMap(Map<Long, Date> source);
}
映射流 :
@Mapper
public interface CarMapper {
Set<String> integerStreamToStringSet(Stream<Integer> integers);
List<CarDto> carsToCarDtos(Stream<Car> cars);
CarDto carToCarDto(Car car);
}
@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);
}
默认值和常量 :
@Mapper(uses = StringListMapper.class)
public interface SourceTargetMapper {
SourceTargetMapper INSTANCE = Mappers.getMapper(SourceTargetMapper.class);
@Mappings({
@Mapping(target = "stringProperty", source = "stringProp", defaultValue = "undefined"),
@Mapping(target = "longProperty", source = "longProp", defaultValue = "-1"),
@Mapping(target = "stringConstant", constant = "Constant Value"),
@Mapping(target = "integerConstant", constant = "14"),
@Mapping(target = "longWrapperConstant", constant = "3001"),
@Mapping(target = "dateConstant", dateFormat = "dd-MM-yyyy", constant = "09-01-2014"),
@Mapping(target = "stringListConstants", constant = "jack-jill-tom")
})
Target sourceToTarget(Source s);
}
表达式 :
@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);
}
@Mapper(uses = FruitFactory.class)
public interface FruitMapper {
@BeanMapping(resultType = Apple.class)
Fruit map(FruitDto source);
}
public class FruitFactory {
public Apple createApple() {
return new Apple("Apple");
}
public Banana createBanana() {
return new Banana("Banana");
}
}
@MapperConfig(uses = CustomMapperViaMapperConfig.class, unmappedTargetPolicy = ReportingPolicy.ERROR)
public interface CentralConfig {}
@Mapper(config = CentralConfig.class, uses = { CustomMapperViaMapper.class } )
public interface SourceTargetMapper {}
本文作者:云杨四海 原文链接:对象拷贝 - 优雅的解决方案 Mapstruct 版权归作者所有,转载请注明出处