MapStruct的基础⽤法详解
这⾥写⽬录标题
⼀、MapStruct是什么
1.1 使⽤背景
1. 需求场景多。
在我们⽇常的Java开发⼯作中,经常会遇到模型对象的转化,例如从实体类转化为DTO模型,DTO转化为VO、TO等模型的业务场景
2. 模型转化枯燥、编码价值低。⽽模型的转化是枯燥且⽆技术含量的,不仅耗费⼤量时间⽽且很容易出错
3. 问题排查困难。若涉及多个具有很多字段的Bean的模型转化时,不得不排查是否两个模型的相同字段的转化映射关系有缺失
1.2 优势
1. MapStruct 是⼀个 Java注释处理器,⽤于⽣成类型安全的 bean 映射类。
2. 您所要做的就是定义⼀个Bean的映射抽象类,在该抽象类中声明任何所需的映射⽅法。
3. 在编译期间,MapStruct 将⽣成此抽象类的实现类。这个实现使⽤普通的 Java ⽅法调⽤来映射源对象和⽬标对象。
4. 与⼿动编写映射代码相⽐,MapStruct 通过⽣成编写繁琐且容易出错的代码来节省时间。
5. 与动态映射框架相⽐,MapStruct 具有以下优势:
(1)通过使⽤普通⽅法getter、setter调⽤,⽽不是反射来快速执⾏,效率很⾼。
(2)编译时类型安全:只能映射相互映射的对象和属性,不会将其余模型属性进⾏映射
⼆、基础⽤法详解
2.1 定义映射器
2.1.1 基本映射
要创建映射器,只需使⽤所需的映射⽅法定义⼀个 Java 接⼝并使⽤注释对其进⾏org.mapstruct.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);
}
MapStruct 的⼀般理念是⽣成的代码看起来尽可能像您⾃⼰亲⼿编写的代码。特别是这意味着通过简单的 getter/setter 调⽤⽽不是反射或类似⽅法将值从源复制到⽬标。
2.1.2 具有多个源参数的映射⽅法
MapStruct 还⽀持具有多个源参数的映射⽅法。这很有⽤,例如,为了将多个实体组合成⼀个数据传输对象。
@Mapper
public interface AddressMapper {
@Mapping(target ="description", source ="person.description")
@Mapping(target ="houseNumber", source ="address.houseNo")
DeliveryAddressDto personAndAddressToDeliveryAddressDto(Person person, Address address);
}
显⽰的映射⽅法采⽤两个源参数并返回⼀个组合的⽬标对象。与单参数映射⽅法⼀样,属性按名称映射。
如果多个源对象定义了具有相同名称的属性,则必须使⽤@Mapping注释指定从中检索属性的源参数,
如⽰例中的description属性所⽰。当这种歧义未解决时,将引发错误。对于在给定源对象中只存在⼀次的属性,可以选择指定源参数的名称,因为它可以⾃动确定。
2.1.3 将嵌套的 bean 属性映射到当前⽬标
如果您不想显式命名嵌套源 bean 中的所有属性,则可以将其.⽤作⽬标。这将告诉 MapStruct 将每个属性从源 bean 映射到⽬标对象。
@Mapper
public interface CustomerMapper {
@Mapping( target ="name", source ="record.name")
@Mapping( target =".", source ="record")
@Mapping( target =".", source ="account")
Customer customerDtoToCustomer(CustomerDto customerDto);
}
⽣成的代码将直接映射从d到的每个属性Customer,⽆需⼿动命名它们中的任何⼀个,也是如此
Customer.account。
当存在冲突时,可以通过显式定义映射来解决这些冲突。例如在上⾯的例⼦中。name发⽣在d和 中CustomerDto.account。映射@Mapping( target = “name”, source = “record.name” )解决了这个冲突。
2.1.4 Map到Bean的映射
public class Customer {
private Long id;
private String name;
//getters and setter omitted for brevity
}
@Mapper
public interface CustomerMapper {
@Mapping(target ="name", source ="customerName")
Customer toCustomer(Map<String, String> map);
}
2.1.4.1 Map映射到Bean的实现类
public class CustomerMapperImpl implements CustomerMapper {
@Override
public Customer toCustomer(Map<String, String> map){
// ...
if( ainsKey("id")){
customer.setId( Integer.parseInt( ("id")));
}
if( ainsKey("customerName")){
customer.setName( ("customerName"));
}
// ...
}
}
2.2 数据类型转化
源对象和⽬标对象中的映射属性并不总是具有相同的类型。例如,属性可能int属于源 bean 中的类型Long,但属于⽬标 bean 中的类型。
另⼀个例⼦是对其他对象的引⽤,这些对象应该映射到⽬标模型中的相应类型。例如,类Car可能具有在映射对象时需要转换为对象driver 的类型的属性。PersonPersonDtoCar
2.2.1 隐式类型转化
在许多情况下,MapStruct 会⾃动处理类型转换。例如,如果⼀个属性int在源 bean 中属于类型但String在⽬标 bean 中属于类型,则⽣成的代码将分别通过调⽤String#valueOf(int)和透明地执⾏转换Integer#parseInt(String)(⾃动完成转化)
⽬前⾃动应⽤以下转换:
之间的所有Java基本数据类型及其相应的包装类型,例如之间int和Integer,boolean和Boolean等⽣成的代码是null转换⼀个包装型成相应的原始类型时⼀个感知,即,null检查将被执⾏。
在所有 Java 原始数字类型和包装器类型之间,例如在int和long或byte和之间Integer。
在所有 Java原始类型(包括它们的包装器)和String之间,例如在int和String或Boolean和之间String。DecimalFormat 可以指定由
理解的格式字符串。
2.1.1.1 从int到String的转换
@Mapper
public interface CarMapper {
@Mapping(source ="price", numberFormat ="$#.00")
CarDto carToCarDto(Car car);
@IterableMapping(numberFormat ="$#.00")
List<String>prices(List<Integer> prices);
}
2.1.1.2 从 BigDecimal 到 String 的转换
@Mapper
public interface CarMapper {
@Mapping(source ="power", numberFormat ="#.##E0")
CarDto carToCarDto(Car car);
}
2.1.1.3 从⽇期到字符串的转换
@Mapper
public interface CarMapper {
@Mapping(source ="manufacturingDate", dateFormat ="")抽象类的使用
CarDto carToCarDto(Car car);
@IterableMapping(dateFormat ="")
List<String>stringListToDateList(List<Date> dates);
}
2.2.2 控制嵌套的 bean 映射
在最简单的情况下,嵌套级别上有⼀个属性需要更正。以fish在FishTankDto和 中具有相同名称的属性为例FishTank。对于这个属性MapStruct ⾃动⽣成⼀个映射:FishDto fishToFishDto(Fish fish)。MapStruct 不可能知道有偏差的属性kind和type。因此,这可以在映射规则中解决:@Mapping(target=“fish.kind”, source=“pe”). 这告诉 MapStruct 偏离kind在此级别查名称并将其映射到type.
2.2.2.1 控制嵌套 bean 映射的映射器 I
@Mapper
public interface FishTankMapper {
@Mapping(target ="fish.kind", source ="pe")
@Mapping(target ="fish.name", ignore =true)
@Mapping(target ="ornament", source ="ament")
@Mapping(target ="material.materialType", source ="material")
@Mapping(target ="anisation.name", source ="anisationName")
FishTankDto map( FishTank source );
}
1. 相同的构造可⽤于在嵌套级别忽略某些属性,如第⼆条@Mapping规则所⽰。
2. 当源和⽬标不共享相同的嵌套级别(相同数量的属性)时,MapStruct
甚⾄可以⽤于“挑选”属性。这可以在源和⽬标类型中完成。这在接下来的 2条规则中得到了证明:
@Mapping(target=“ornament”,
source=“ament”)和@Mapping(target=“material.materialType”,
source=“material”)。
2.2.2.2 控制嵌套 bean 映射的映射器 II
@Mapper
public interface FishTankMapperWithDocument {
@Mapping(target ="fish.kind", source ="pe")
@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 ="port")
@Mapping(target ="anisation.name", constant ="NoIdeaInc")
FishTankWithNestedDocumentDto map( FishTank source );
}
请注意@Mapping(target=“quality.document”, source=“port”). DocumentDto在⽬标端不存在。
它是从Report. MapStruct 继续在这⾥⽣成映射代码。该映射本⾝可以被引导到另⼀个名称。这甚⾄适⽤于常量和表达式。这在最后⼀个例⼦中显⽰:
@Mapping(target=“anisation.name”, constant=“NoIdeaInc”).
2.3 集合映射
1. 集合类型(的映射List,Set等等)以相同的⽅式映射⾖类型,即通过定义与在映射器接⼝所需的源和⽬标类型的映射⽅法进⾏。
MapStruct ⽀持来⾃Java Collection Framework 的各种可迭代类型。
2. ⽣成的代码将包含⼀个循环,循环遍历源集合,转换每个元素并将其放⼊⽬标集合。如果在给定的映射器或其使⽤的映射器中到集
合元素类型的映射⽅法,则调⽤此⽅法来执⾏元素转换。或者,如果存在源元素和⽬标元素类型的隐式转换,则将调⽤此转换例程。具有集合映射⽅法的映射器
@Mapper
public interface CarMapper {
Set<String>integerSetToStringSet(Set<Integer> integers);
List<CarDto>carsToCarDtos(List<Car> cars);
CarDto carToCarDto(Car car);
}
⽣成的实现为每个元素integerSetToStringSet执⾏从Integer到到的转换String,⽽⽣成carsToCarDtos()的carToCarDto()⽅法为每个包含的元素调⽤⽅法,如下所⽰:
⽣成的集合映射⽅法
@Override
public Set<String>integerSetToStringSet(Set<Integer> integers){
if( integers ==null){
return null;
}
Set<String> set =new LinkedHashSet<String>();
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<CarDto>();
for( Car car : cars ){
list.add(carToCarDto( car ));
}
return list;
}//GENERATED CODE
carDto.setPassengers(personsToPersonDtos( Passengers()));
...
在映射 bean 的集合类型属性时,例如从Car#passengers(类型List)到CarDto#passengers(类型List),MapStruct 将寻具有匹配参数和返回类型的集合映射⽅法。
使⽤集合映射⽅法来映射 bean 属性
//GENERATED CODE
carDto.setPassengers(personsToPersonDtos( Passengers()));
...
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论