MapStruct应用于对象属性拷贝,其原理是调用源或目标对象的getter/setter方法实现属性拷贝,而不是使用反射来实现属性拷贝。对于属性是public类型,源/目标对象不必拥有getter/setter方法。其具有以下优点:
1. 对于映射属性类型不匹配,可实现自动转型或指定转型方式
2. 构建时清除错误报告。对于目标对象属性未映射成功,可选多种策略。
使用准备步骤
pom.xml文件中加入引用
<properties>
<org.mapstruct.version>1.2.0.Final</org.mapstruct.version>
</properties>
<dependencies>
<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>
</dependencies>
简单使用
@Mapper
public interface TestMapper{
TestMapper INSTANCE=Mappers.getMapper(TestMapper.class);
@Mappings({@Mapping(target="id",source="testId",ignore=false)})
UserDTO convert(UserDO userDO);
}
使用时TestMapper.INSTANCE.convert(xxx)即可将UserDO的属性赋值转换为UserDTO
@Mapper
Mapper注解用来标注某个抽象类或接口是映射器,这个类或接口会被MapStruct实现成对象拷贝的映射器。该注解的主要属性
属性名 | 取值作用 | 默认值 |
---|---|---|
componentModel | default:映射器不使用组件模型,通常通过实例检索实例 Mappers#getMapper(Class) 。 spring: 生成的映射器上被打了@Compoent注解,若spring注解扫描了此类,可以使用@Autowired等注解获取到映射器实例 | default |
unmappedTargetPolicy | 目标属性未被映射到时的策略。WARN:发出警告。ERROR:抛出异常。IGNORE:忽略 | WARN |
uses | 自定义映射类型不匹配时的转型关系映射器 | {} |
@Mappings & @Mapping
用于定义映射关系。若映射关系简单属性类型都互相匹配,则无需使用该注解。只有一个属性需要指明映射关系则直接在该方法上打上@Mapping。对于多个属性需要指明则使用简单使用的代码方式。target是目标属性的意思,source是源目标属性。简单使用的例子是将testId属性映射到id上。ignore决定在target未找到映射时是否忽略,false未不忽略,也是默认选择。
Mappers
Mappers.getMapper(Class<?> clazz)可直接或者映射器的实例。这个方法是固定也是最常用的
自定义方法
@Mapper
public interface TestMapper{
TestMapper INSTANCE=Mappers.getMapper(TestMapper.class);
@Mappings({@Mapping(target="id",source="id")})
UserDTO convert(UserDO userDO);
/**该方法生成的映射器不会覆盖,对于抽象类中的实例方法也不会被覆盖。由此可以实现一些特殊化的映射方式*/
default UserDTO convert(UserDO userDO){
UserDTO userDTO=convert(userDO);
//操作userDTO,做一些特殊映射
return userDTO;
}
}
多源参数映射
@Mapper
public interface TestMapper{
TestMapper INSTANCE=Mappers.getMapper(TestMapper.class);
@Mappings({@Mapping(target="name",source="person.name"),@Mapping(target="addressRoad",source="address.road")})
UserInfo convert(Person person,Address address);
}
上面将person和address的属性复制给了UserInfo,这里必须要指明映射关系。将address的road属性映射给addressRoad属性。若UserInfo中有个AddressInfo addressInfo属性。AddressInfo中有个road属性。则target=“addressInfo.road”。若UserInfo中有个Address addressTest属性,则最好指定@Mapping(target=“addressTest”,source=“address”)来明确映射关系。
更新目标
上面的例子都是MapStruct为我们创建好了目标对象,有时候目标对象已经存在,我们只需要将一些属性复制给它,可给目标对象打上@MappingTarget
@Mapper
public interface TestMapper{
TestMapper INSTANCE=Mappers.getMapper(TestMapper.class);
/**方法上可打上@Mapping来指定映射关系*/
void update(UserDO userDO,@MappingTarget UserDTO userDTO);
}
numberFormat & dateFormat
MapStruct支持源映射目标的特殊函数。numberFormat对应java.text.DecimalFormat,dateFormat对应java.text.SimpleDateFormat。它们都是用于源向目标(字符串)的转换
@Mapper
public interface TestMapper{
TestMapper INSTANCE=Mappers.getMapper(TestMapper.class);
@Mappings({@Mapping(target="date",source="date",dateFormat="yyyy-MM-dd"),@Mapping(source="salary",target="salary",numberFormat="$##0.00")})
UserDTO convert(UserDO userDO);
}
自定义映射
@Mapper的uses属性用来指定隐式的转换映射。
class A{
public String dateAsString(Date date){
return "自定义的转换方式"+date.getTime();
}
}
@Mapper(uses=A.class)
public interface TestMapper{
TestMapper INSTANCE=Mappers.getMapper(TestMapper.class);
@Mappings({@Mapping(target="date1",source="date"})
UserDTO convert(UserDO userDO);
}
上例中的userDO中的Date转String会按照A#dateAsString方法进行转换
集合映射
如List<UserDO>转List<UserDTO>,要先定义好UserDO向UserDTO转换的方法。然后定义的List映射方法会按照这个方法进行映射
@Mapper
public interface TestMapper{
TestMapper INSTANCE=Mappers.getMapper(TestMapper.class);
/**UserDO向UserDTO转换的方法*/
UserDTO convert(UserDO userDO);
/**这个方法就是MapStruct生成的映射方法,不必声明为default,default的代码只是用来说明生成后的效果*/
default List<UserDTO> convertList(List<UserDO> list){
List<UserDTO> li=null;
if(list!=null){
li=new ArrayList<>();
list.forEach(c->{
li.add(convert(c));
});
}
return li;
}
/**Set的转换,同上*/
Set<UserDTO> convertSet(Set<UserDO> set);
/**对于String、Integer这种具有隐式转换的,不必先声明互转方法*/
List<String> convert2(List<Integer> li);
/**映射流和映射List一样,利用先定义好的convert方法*/
List<UserDTO> convertStream(Stream<UserDO> stream);
}
还有@IterableMapping用法
@Mapper
public interface TestMapper{
TestMapper INSTANCE=Mappers.getMapper(TestMapper.class);
@IterableMapping(dateFormat="yyyy/MM/dd")
List<String> convert(List<Date> dates);
}
映射Map
需要用到@MapMapping注解
@Mapper
public interface TestMapper{
TestMapper INSTANCE=Mappers.getMapper(TestMapper.class);
@MapMapping(valueDateFormat="yyyy.MM.dd")
Map<String,String> convert(Map<Long,Date> map);
}
映射枚举
使用@ValueMappings及@ValueMapping,如果两个枚举之间的枚举名称一一对应,则不必使用这两个注解。
@Mapper
public interface TestMapper{
TestMapper INSTANCE= Mappers.getMapper(TestMapper.class);
@ValueMappings({
@ValueMapping(target="B",source="A"),
@ValueMapping(target="D",source="C")
})
Enum2 convert(Enum1 enum1);
}
这样在调用convert(Enum1.A)时会返回Enum2.B。
constant & defaultvalue
constant表示不论源对象中的对应属性值时什么,目标对象的值都是constant的值。defaultvalue表示如果映射关系存在且源对象中属性值为null则使用defaultvalue值代替
@Mapper
public interface TestMapper{
TestMapper INSTANCE= Mappers.getMapper(TestMapper.class);
@Mapping(target="name",constant="覆盖值")
UserDTO convert(UserDO userDO);
}
其他映射请见参考文档