-
参考视频 ------ 参考demo ------- mapStruct官方文档
-
概述:
1).mapStruct和beanUtils一样,都是浅复制,具体参照本人深/浅复制文章
2).mapStruct相对于beanUtils,可以复制更为复杂的属性,不同类型,不同名之间也可以映射,以及可以实现实体的批量转换 -
具体使用:
1).注意事项:
①lombok版本必须是1.16.16以上,推荐1.18.10
②mapstract 版本推荐 1.3.1.Final 及以上
③低于以上的版本会出现各种各样的问题,这都是本人踩过的坑
2)准备工具:
①编译器必须安装lombok插件,导入的lombok相关jar包才能正常使用

②可以安装mapStruct插件来简化代码,本人没有安装,下面代码也是在无mapStruct插件情况下的代码

3)注解及相关介绍
1.默认的映射规则:
1).同类型且同名的属性,会自动映射
2).同名但类型不同,会自动进行类型转换,支持的类型有:
①8种基本类型以及包装类型,String类型之间的转换;
②日期类型和String之间;
2.@Mapper(componentModel = “spring”) 注意这个mapper是mapStruct包下面的,不是mybatis下的
实质就是加了@comment注解
添加componentModel=“spring”就可以是使工具类和Spring结合,利用注入来使用,不添加的话是不会被注入到spring中的
但是@mapper必须要加,这是工具类的入口
添加componentModel=“spring”后,就可以在工具类里使用spring的资源,如controller,server,mapper等资源,其他类也可以像调用controller,server,mapper等那样,利用@Autowired的方式,去定义调用
@Mappings 多个映射规则的集合
@Mapping 映射规则集合中的某一个规则
source 源类中的属性名
target 目标类中的属性名
dateFormat 源→目标日期格式化 使用方式:dateFormat = “yyyy-MM-dd HH:mm:ss”
numberFormat 源→目标数字格式化 使用方式:numberFormat = “#.00”
ignore 忽略掉某属性的复制 使用方式:ignore = true 则该属性被复制忽略
@AfterMapping 表示让mapstruct在调用完自动转换方法之后,会来自动调用本方法
@MappingTarget:表示传来的实体是已经转换后的,是赋值后的,配合AfterMapping 一起使用
@BeanMapping(ignoreByDefault = true) 禁止默认配置,只支持自定义的映射行为 即上述的默认规则失效,配合@Mapping来完成自定义的复制效果
@InheritConfiguration 继承上一转换的配置 如果上一次转换屏蔽了默认的配置规则,那么这次也会屏蔽掉默认的配置规则,如果上一次定义了其他规则,那么这一次也会执行该规则
@InheritInverseConfiguration(name = “XXX”)指定继承那个方法的配置,XXX为某方法名,该注解只会继承指定方法,source和target之间的映射关系,也可以反着,但是不会继承@BeanMapping(ignoreByDefault = true)之类的配置
4)使用步骤
概述:其实就是创建一个配置类,用这个配置类去实现相关属性的映射
①.导入依赖
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mapstruct/mapstruct -->
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.3.1.Final</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mapstruct/mapstruct-processor -->
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.3.1.Final</version>
</dependency>
</dependencies>
②.2.新建一个抽象类或者接口,作为映射的配置类,并标注@mapper-----mapstruct包下的注解 ,来表示这是mapStruct的映射配置
③.写对应的转换方法和映射规则,并创建本类的静态调用实现
④.获取对象并实现。
5)相关注解的使用例子
①.首先是例子中要用到的实体
package com.example.demo.beans;
import lombok.Data;
import java.util.Date;
import java.util.List;
@Data
public class CarDTO {
private Long id;
private String vin;
//添加可变类,验证是否是深复制
private StringBuilder name;
private double price;
private double totalPrice;
private Date publishDate;
private String color;
private String brand;
private List<PartDTO> partsDTOS;
private DriverDTO driverDTO;
}
package com.example.demo.beans;
import lombok.Data;
@Data
public class DriverDTO {
private Long id;
private String name;
}
package com.example.demo.beans;
import lombok.Data;
@Data
public class PartDTO {
private Long partId;
private String partName;
}
package com.example.demo.vo;
import lombok.*;
@Data
public class CarVO {
private Long id;
private String vin;
//添加可变类,验证是否是深复制
private StringBuilder name;
private Double price;
private String totalPrice;
private String publishDate;
private String color;
private String brandName;
private Boolean hasPart;
private DriverVO deiverVO;
}
package com.example.demo.vo;
import lombok.Data;
@Data
public class DriverVO {
private Long deiverId;
private String fullName;
}
package com.example.demo.vo;
import lombok.Data;
@Data
public class partVO {
private Long partId;
private String partName;
}
package com.example.demo.vo;
import lombok.Data;
@Data
public class VehicleVO {
private Long id;
private Double price;
private String brandName;
}
②.创建配置抽象类
package com.example.demo.convert;
/*
* 使用mapStruct步骤
* 1.导入依赖
* 2.新建一个抽象类或者接口,并标注@mapper-----mapstruct包下的注解
* 3.写一个转换方法
* 4.获取对象并使用
* */
import com.example.demo.beans.CarDTO;
import com.example.demo.beans.DriverDTO;
import com.example.demo.beans.PartDTO;
import com.example.demo.service.UserService;
import com.example.demo.vo.CarVO;
import com.example.demo.vo.DriverVO;
import com.example.demo.vo.VehicleVO;
import org.mapstruct.*;
import org.mapstruct.factory.Mappers;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.List;
import java.util.Objects;
//添加componentModel=spring就可以是使工具类和Spring结合,就可以利用注入来使用
//不添加的话是不会被注入到spring中的
@Mapper(componentModel = "spring")
public abstract class CarConvert {
public static CarConvert INSTANCE = Mappers.getMapper(CarConvert.class);//可以静态的调用
/*
待填写内容
*/
}
③.创建测试类,用到spring注入的在项目的测试文件夹下
@SpringBootTest(classes = {MapStructApplication.class})
public class testSomeThing {
//注入的方式调用工具类
@Autowired
private CarConvert carConvert;
/*
待填写内容
*/
private CarDTO buildCarDTO() {
//初始化零件
//零件一
PartDTO partDtO1 = new PartDTO();
partDtO1.setPartId(1L);
partDtO1.setPartName("多功能方向盘");
//零件二
PartDTO partDtO2 = new PartDTO();
partDtO1.setPartId(2L);
partDtO1.setPartName("智能车门");
//填充至list中
List<PartDTO> partDTOList = new ArrayList<>();
partDTOList.add(partDtO1);
partDTOList.add(partDtO2);
//初始化司机
DriverDTO driverDTO = new DriverDTO();
driverDTO.setId(1L);
driverDTO.setName("王二狗");
//然后把初始化后的全部填充至实体中
CarDTO carDTO = new CarDTO();
carDTO.setId(330L);
carDTO.setVin("vin123456789");
carDTO.setName(new StringBuilder("可口可乐"));
carDTO.setPrice(123789.126d);
carDTO.setTotalPrice(143789.126d);
carDTO.setPublishDate(new Date());
carDTO.setColor("白色");
carDTO.setBrand("大众");
carDTO.setPartsDTOS(partDTOList);
carDTO.setDriverDTO(driverDTO);
return carDTO;
}
}
⑤测试1:@mappings和@afterMapping的使用
配置类里面写
/*
*
*carDTO ----> carVO
* */
//Mappings 指定映射规则 ,不同属性名之间的映射,属性之间映射格式问题,如时间到字符串格式问题
@Mappings(
value = {
@Mapping(source = "totalPrice", target = "totalPrice", numberFormat = "#.00"),//保留两位小数
@Mapping(source = "publishDate", target = "publishDate", dateFormat = "yyyy-MM-dd HH:mm:ss"),
@Mapping(target = "color",ignore = true),//忽略这个属性的映射
@Mapping(source = "brand",target = "brandName"),//不同名之间的映射
@Mapping(source = "driverDTO",target = "deiverVO")//必须有转换的方法driverDTO2DriverVO
}
)
public abstract CarVO abc(CarDTO carDTO);
@AfterMapping//表示让mapstruct在调用完自动转换方法之后,会来自动调用本方法
public void dto2voAfter(CarDTO carDTO, @MappingTarget CarVO carVO){
//@MappingTarget:表示传来的carVO是已经转换后的,是赋值后的
System.out.println("========执行AfterMapping==============");
List<PartDTO> partsDTOS = carDTO.getPartsDTOS();
carVO.setHasPart(Objects.nonNull(partsDTOS));
};
然后测试类里面写
/**
* 测试@mappings进行单体的转换,同时转换完后CarVO会执行AfterMapping
*/
@Test
public void test2() {
CarDTO carDTO = buildCarDTO();
CarVO carVO = carConvert.abc(carDTO);
//改变可变量的值,观察是否是深复制
StringBuilder name = carVO.getName();
name.append("公司");
//很明显是浅复制
System.out.println(carVO.toString());
System.out.println(carDTO.toString());
}
输出为:
========执行AfterMapping==============
CarVO(id=330, vin=vin123456789, name=可口可乐公司, price=123789.126, totalPrice=143789.13, publishDate=2021-02-20 03:21:05, color=null, brandName=大众, hasPart=true, deiverVO=DriverVO(deiverId=1, fullName=王二狗))
CarDTO(id=330, vin=vin123456789, name=可口可乐公司, price=123789.126, totalPrice=143789.126, publishDate=Sat Feb 20 03:21:05 CST 2021, color=白色, brand=大众, partsDTOS=[PartDTO(partId=2, partName=智能车门), PartDTO(partId=null, partName=null)], driverDTO=DriverDTO(id=1, name=王二狗))
⑥测试二:@mapping的单独使用
配置类里面写
/**
* driverDTO -> DriverVO
* @param driverDTO
* @return
*/
@Mapping(source = "id",target = "deiverId")
@Mapping(source = "name",target = "fullName")
public abstract DriverVO driverDTO2DriverVO(DriverDTO driverDTO);
测试类里面写
/**
* 测试@mapping单独使用
*/
@Test
public void test2_1(){
DriverDTO peter = new DriverDTO();
peter.setId(12L);
peter.setName("peter");
DriverVO driverVO = carConvert.driverDTO2DriverVO(peter);
System.out.println("==driverVO=="+driverVO.toString());
}
输出为:
==driverVO==DriverVO(deiverId=12, fullName=peter)
⑦测试三:这个批量转换会先在配置类里面寻找单体转换,利用配置类里的单体转换,来完成批量转换
配置类里写
/**
* dto2vo这个方法的批量转换
* 这个批量转换会先在配置类里面寻找单体转换,利用配置类里的单体转换,来完成批量转换
*/
public abstract List<CarVO> dtos2vos(List<CarDTO> carDTOs);
测试文件里写
/**
* 测试批量转换
* list<CarDTO> → list<CarVO></></>
*/
@Test
public void test3() {
CarDTO carDTO = buildCarDTO();
List<CarDTO> carDTOLists = new ArrayList<>();
carDTOLists.add(carDTO);
carDTOLists.add(carDTO);
carDTOLists.add(carDTO);
List<CarVO> carVOList = CarConvert.INSTANCE.dtos2vos(carDTOLists);
System.out.println(carVOList);
}
输出为:
========执行AfterMapping==============
========执行AfterMapping==============
========执行AfterMapping==============
[CarVO(id=330, vin=vin123456789, name=可口可乐, price=123789.126, totalPrice=143789.13, publishDate=2021-02-20 03:22:42, color=null, brandName=大众, hasPart=true, deiverVO=DriverVO(deiverId=1, fullName=王二狗)), CarVO(id=330, vin=vin123456789, name=可口可乐, price=123789.126, totalPrice=143789.13, publishDate=2021-02-20 03:22:42, color=null, brandName=大众, hasPart=true, deiverVO=DriverVO(deiverId=1, fullName=王二狗)), CarVO(id=330, vin=vin123456789, name=可口可乐, price=123789.126, totalPrice=143789.13, publishDate=2021-02-20 03:22:42, color=null, brandName=大众, hasPart=true, deiverVO=DriverVO(deiverId=1, fullName=王二狗))]
⑧测试@BeanMapping忽略默认配置规则,顺便测试@InheritConfiguration继承配置注解
配置文件里写
/**
* 配置忽略mapstruct的默认映射行为,只映射那些配置了@Mapping的属性
*@BeanMapping
*/
@BeanMapping(ignoreByDefault = true)//禁止默认配置,只支持自定义的映射行为
@Mapping(source = "id",target = "id")//只映射id
@Mapping(source = "brand",target = "brandName")//只映射brand
public abstract VehicleVO carDTO2vehicleVO(CarDTO carDTO);
@InheritConfiguration
//继承上一转换的配置,例如在测试的例子中,上一次的配置是屏蔽掉默认配置,值转换ID和brandName
public abstract void updatevehicleVO(CarDTO carDTO,@MappingTarget VehicleVO vehicleVO);
测试文件里写
/**
* 测试 @BeanMapping
*/
@Test
public void test4() {
CarDTO carDTO = buildCarDTO();
VehicleVO vehicleVO = CarConvert.INSTANCE.carDTO2vehicleVO(carDTO);
System.out.println("========"+vehicleVO.toString());
}
/**
* 测试 @InheritConfiguration 继承配置
*/
@Test
public void test5() {
CarDTO carDTO = buildCarDTO();
VehicleVO vehicleVO = CarConvert.INSTANCE.carDTO2vehicleVO(carDTO);
System.out.println("========"+vehicleVO.toString());
CarDTO carDTO1 = new CarDTO();
carDTO1.setBrand("迈巴赫");
//通过carDTO1的属性值来更新已存在的VehicleVO对象
//继承上一转换的配置,例如在测试的例子中,上一次的配置是屏蔽掉默认配置,值转换ID和brandName
//所以在输出结果中看不到price的值
CarConvert.INSTANCE.updatevehicleVO(carDTO1,vehicleVO);
System.out.println("========"+vehicleVO.toString());
}
输出为:
========VehicleVO(id=330, price=null, brandName=大众)
========VehicleVO(id=330, price=null, brandName=大众)
========VehicleVO(id=null, price=null, brandName=迈巴赫)
⑨,测试InheritInverseConfiguration继承特定类的配置
配置文件里写
/**
* 测试 @InheritInverseConfiguration 反向继承
*name :指定使用哪一个方法的配置
*/
@BeanMapping(ignoreByDefault = true)//禁止默认配置,只支持自定义的映射行为
@InheritInverseConfiguration(name = "carDTO2vehicleVO")
//指定继承哪一个方法的配置,继承@mapping注解且source和target是相反的
//不会继承@BeanMapping等其他注解
public abstract CarDTO vehicleVO2CarDTO(VehicleVO vehicleVO);
测试文件里写
/**
* 测试 @InheritInverseConfiguration 反向继承
*/
@Test
public void test6() {
VehicleVO vehicleVO = new VehicleVO();
vehicleVO.setId(9999L);
vehicleVO.setBrandName("别克");
vehicleVO.setPrice(66554322d);
CarDTO carDTO = CarConvert.INSTANCE.vehicleVO2CarDTO(vehicleVO);
System.out.println(carDTO);
}
输出为:
CarDTO(id=9999, vin=null, name=null, price=0.0, totalPrice=0.0, publishDate=null, color=null, brand=别克, partsDTOS=null, driverDTO=null)

本文介绍了如何结合MapStruct和Lombok进行深度定制的属性复制,包括不同名称和类型的属性映射,以及如何在Spring中集成MapStruct。文章详细讲解了注意事项、所需的Lombok和MapStruct版本,以及@Mapper、@Mapping等相关注解的用法,并通过多个测试案例展示了映射规则的实践应用。
698

被折叠的 条评论
为什么被折叠?



