什么是MapStruct?
MapStruct是用于生成类型安全的bean映射类的Java注解处理器。
你所要做的就是定义一个映射器接口,声明任何需要映射的方法。在编译过程中,MapStruct将生成该接口的实现。此实现使用纯Java的方法调用源对象和目标对象之间进行映射,并非Java反射机制。
与手工编写映射代码相比,MapStruct通过生成冗长且容易出错的代码来节省时间。在配置方法的约定之后,MapStruct使用了合理的默认值,但在配置或实现特殊行为时将不再适用。
与动态映射框架相比,MapStruct具有以下优点:
1,使用纯Java方法代替Java反射机制快速执行
2,编译时类型安全:只能映射彼此的对象和属性,不能映射一个Order实体到一个Customer DTO中等等
3,如果无法映射实体或属性,则在编译时清除错误报告
maven依赖导入
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.3.1.Final</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.3.1.Final</version>
<scope>compile</scope>
</dependency>
</dependencies>
<build>
<plugins>
<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>
</configuration>
</plugin>
</plugins>
</build>
直接上代码
首先定义一个泛型的基础映射器BaseMapping
/**
* <p>
* MapStruct映射基类,直接继承接口即可使用通用方法,针对性指定需在继承类的接口方法上指定
*
* 例: public interface xxxMapping extends BaseMapping<xxxDO, xxxDTO>
*
* 具体用法可参考单元测试,建议配合MapStruct Support插件使用
*/
@MapperConfig(componentModel = "spring")
public interface BaseMapping<S, T> {
/** 正向映射 */
@InheritConfiguration
T convertTo(S obj);
/** 反向映射 */
@InheritInverseConfiguration(name = "convertTo")
S convertFrom(T obj);
/** 正向映射(List) */
default List<T> convertTo(List<S> list) {
if (list == null) {
return null;
}
List<T> result = new ArrayList<T>(list.size());
for (S s : list) {
if (s == null) {
continue;
}
result.add(convertTo(s));
}
return result;
}
/** 反向映射(List) */
default List<S> convertFrom(List<T> list) {
if (list == null) {
return null;
}
List<S> result = new ArrayList<S>(list.size());
for (T t : list) {
if (t == null) {
continue;
}
result.add(convertFrom(t));
}
return result;
}
/** 正向映射的后置处理,List映射会自动继承此配置 */
@AfterMapping
default void handleAfterConvertTo(S src, @MappingTarget T dest) {
afterConvertTo(src, dest);
}
/** 反向映射的后置处理,List映射会自动继承此配置 */
@AfterMapping
default void handleAfterConvertFrom(T src, @MappingTarget S dest) {
afterConvertFrom(src, dest);
}
/** 正向映射的后置处理,List映射会自动继承此配置 */
default void afterConvertTo(S src, T dest) {
//TODO 覆盖此方法处理其他复杂转换逻辑
}
/** 反向映射的后置处理,List映射会自动继承此配置 */
default void afterConvertFrom(T src, S dest) {
//TODO 覆盖此方法处理其他复杂转换逻辑
}
// /** 正向映射(stream) */
// @InheritConfiguration(name = "convertTo")
// Stream<T> convertTo(Stream<S> stream);
//
// /** 反向映射(stream) */
// @InheritConfiguration(name = "convertFrom")
// Stream<S> convertFrom(Stream<T> stream);
}
@MapperConfig:定义一个映射器配置
@InheritConfiguration:正向映射自动继承转换类型相同属性名相同的字段
@InheritInverseConfiguration:反向映射自动继承转换类型相同属性名相同的字段
@AfterMapping:后置处理器,在自动转换后可以实现不同类型不同字段之间的自定义转换
实现具体的类型转换器
这里实现一个具体的DO—>DTO的类型转换器EditionMapping
/**
* @author shihaowei
* @date 2020/6/30 4:34 下午
*/
@Mapper(componentModel = "spring")
public interface EditionMapping extends BaseMapping<EditionDO, EditionDTO> {
EditionMapping INSTANT = Mappers.getMapper(EditionMapping.class);
@Override
default void afterConvertTo(EditionDO src, EditionDTO dest) {
dest.setOrderArray(src.getOrderarray());
dest.setOrderList(src.getOrderlist());
}
}
用于测试的两个实体类:
EditionDO
public class EditionDO {
private Integer id;
private String name;
private Double memory;
private String orderarray;
private String orderlist;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Double getMemory() {
return memory;
}
public void setMemory(Double memory) {
this.memory = memory;
}
public String getOrderarray() {
return orderarray;
}
public void setOrderarray(String orderarray) {
this.orderarray = orderarray;
}
public String getOrderlist() {
return orderlist;
}
public void setOrderlist(String orderlist) {
this.orderlist = orderlist;
}
@Override
public String toString() {
return "EditionDO{" +
"id=" + id +
", name='" + name + '\'' +
", memory=" + memory +
", orderarray='" + orderarray + '\'' +
", orderlist='" + orderlist + '\'' +
'}';
}
}
EditionDTO
/**
* @author shihaowei
* @date 2020-06-10 16:35
*/
public class EditionDTO {
private Integer id;
private String name;
private String memory;
private String orderArray;
private String orderList;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getMemory() {
return memory;
}
public void setMemory(String memory) {
this.memory = memory;
}
public String getOrderArray() {
return orderArray;
}
public void setOrderArray(String orderArray) {
this.orderArray = orderArray;
}
public String getOrderList() {
return orderList;
}
public void setOrderList(String orderList) {
this.orderList = orderList;
}
@Override
public String toString() {
return "EditionDTO{" +
"id=" + id +
", name='" + name + '\'' +
", memory=" + memory +
", orderArray='" + orderArray + '\'' +
", orderList='" + orderList + '\'' +
'}';
}
}
这里这两个类有两个字段名不同,需要在后置处理器里面去手动转换,只有类型相同属性名相同的属性才会自动转换,测试如下:
这里不同属性名转换成功。
如果相同属性名,但是类型不同的话,转换会出现什么问题呢?然后我把EditionDTO里面memory属性的类型改成Date,而源EditionDO里面memory属性的类型是Double,然后我们进行测试:
@Test
public void getEditionById2(){
EditionDO editionDO = testService.getEditionById(1);
System.err.println(editionDO);
EditionDTO editionDTO = editionMapping.convertTo(editionDO);
System.err.println(editionDTO);
}
这里的解决方法就是自定义转换类型并切去接口继承
DoubleToDateMapping
/**
* @author shihaowei
* @date 2020/7/1 1:55 下午
*/
public interface DoubleToDateMapping {
default Date DoubleToDate(Double memory){
return new Date();
}
}
然后在mapping转化器里面去继承这个接口,然后可以自动把Double类型转换成Date
@Mapper(componentModel = "spring")
public interface EditionMapping extends BaseMapping<EditionDO, EditionDTO>, DoubleToDateMapping {
EditionMapping INSTANT = Mappers.getMapper(EditionMapping.class);
@Override
default void afterConvertTo(EditionDO src, EditionDTO dest) {
dest.setOrderArray(src.getOrderarray());
dest.setOrderList(src.getOrderlist());
}
}
然后我们再次运行测试:
这里显示转换成功了,这里使用Double转Date的例子不太合情理,但是达到的目的就是不同类型的转换
注:针对相同属性字段不同类型的属性转换为String,正对常用的包装类型mapstruct封装李对这些常用类型的转换,不需要自己写转换