java代码简洁-mapstruct
功能介绍:
MapStruct是一个Java注释处理器,用于生成类型安全的bean映射类。简化各种对象的转换。
不使用框架:
1.多而杂得代码与业务逻辑耦合
2.重复的劳动力
mapstruct使用:
1.导入依赖
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.3.1.Final</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.3.1.Final</version>
</dependency>
配合lombok
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
2.插件安装(非必须)–重启idea
实体类准备
汽车DTO对象
@Data
public class CarDTO {
/**
* 编号
*/
private Long id;
/**
* 车辆编号
*/
private String vin;
/**
* 裸车价格
*/
private double price;
/**
* 上路价格
*/
private double totalPrice;
/**
* 生产日期
*/
private Date publishDate;
/**
* 品牌名称
*/
private String brand;
/**
* 汽车包含零件列表
*/
private List<PartDTO> partDTOS;
/**
* 骑车的司机
*/
private DriverDTO driverDTO;
/**
* 车的颜色
*/
private String color;
}
驾驶员DTO对象
@Data
public class DriverDTO {
/**
* id
*/
private Long id;
/**
* 驾驶员名称
*/
private String name;
}
汽车零件DTO对象
@Data
public class PartDTO {
/**
* 汽车零件id
*/
private Long partId;
/**
* 汽车零件名字
*/
private String partName;
}
汽车VO对象
@Data
public class CarVO {
/**
* 编号
*/
private Long id;
/**
* 车辆编号
*/
private String vin;
/**
* 裸车价格
*/
private double price;
/**
* 上路价格,保留两位小数
*/
private String totalPrice;
/**
* 生产日期
*/
private String publishDate;
/**
* 品牌名称
*/
private String brandName;
/**
* 汽车是否包含零件
*/
private Boolean hasPart;
/**
* 骑车的司机
*/
private DriverVO driverVO;
/**
* 车的颜色
*/
private String color;
}
驾驶员VO对象
@Data
public class DriverVO {
/**
* 驾驶员id
*/
private Long driverId;
/**
* 驾驶员名称
*/
private String fullName;
}
零件VO对象
@Data
public class PartVO {
/**
* 汽车零件id
*/
private Long partId;
/**
* 零件名字
*/
private String partName;
}
模拟业务构造出的carDTO对象
private CarDTO buildCarDTO() {
CarDTO carDTO = new CarDTO();
carDTO.setId(330L);
carDTO.setVin("vin12345698");
carDTO.setPrice(123789.685);
carDTO.setTotalPrice(145698.665);
carDTO.setPublishDate(new Date());
PartDTO partDTO1 = new PartDTO();
partDTO1.setPartId(1L);
partDTO1.setPartName("多功能方向盘");
PartDTO partDTO2 = new PartDTO();
partDTO2.setPartId(2L);
partDTO2.setPartName("智能车门");
ArrayList<PartDTO> list = new ArrayList<>();
list.add(partDTO1); list.add(partDTO2);
carDTO.setPartDTOS(list);
DriverDTO driverDTO = new DriverDTO();
driverDTO.setId(2L);
driverDTO.setName("哇哈");
carDTO.setDriverDTO(driverDTO);
carDTO.setColor("白色");
carDTO.setBrand("品牌");
return carDTO;
}
1.默认映射规则
1.1创建一个抽象类或接口并标注@Mapper注解
import org.mapstruct.Mapper;
@Mapper
public abstract class CarConvert {
//获取对象INSTANCE并使用
public static CarConvert INSTANCE = Mappers.getMapper(CarConvert.class);
//转换方法
public abstract CarVO dto2vo(CarDTO carDTO);
}
1.2测试
@Test
public void test2() {
CarDTO carDTO = buildCarDTO();
CarVO carConvert = CarConvert.INSTANCE.dto2vo(carDTO);
System.out.println(carConvert);
}
1.3结果输出
CarVO(id=330, vin=vin12345698, price=123789.685, totalPrice=145698.665, publishDate=22-8-4 上午11:33, brandName=null, hasPart=true, driverVO=null, color=白色)
默认映射规则:
1.同类型,同名的属性会自动映射
2.自动类型转换
2.1基本类型和包装类型会自动转换,
2.28种基本类型(及包装类型)和String
2.3日期类型和String
2.@Mappings指定某个属性的映射规则
1.抽象接口上加@Mappings注解
//source和target多余的属性对方没有,不会报错
//source映射源属性,target目标映射属性
//numberFormat = "#.00" 数字格式化,保留两位小数
//dateFormat = "yyy-MM-dd HH:mm:ss" 日期格式化
//ignore=true 不映射该属性
@Mappings(value = {
@Mapping(source = "totalPrice", target = "totalPrice", numberFormat = "#.00"),
@Mapping(source = "publishDate", target = "publishDate", dateFormat = "yyy-MM-dd HH:mm:ss"),
@Mapping(target = "color", ignore=true),
@Mapping(source = "brand",target = "brandName"),//属性名称不一样,也可以匹配
@Mapping(source = "driverDTO",target = "driverVO")//对象转化,通过driverDTO2DriverVO方法实现
})
public abstract CarVO dto2vo(CarDTO carDTO);
2.也可以单独写@Mapping
//driverDTO--driverVO
@Mapping(source = "id",target = "driverId")
@Mapping(source = "name",target = "fullName")
public abstract DriverVO driverDTO2DriverVO(DriverDTO driverDTO);
3.@AfterMapping在映射的最后一步对属性进行自定义映射处理
@AfterMapping //表示mapstruct在调用完自动转换的方法后,会来自动调用本方法
//@MappingTarget 表示传来的carVO对象是已经赋值过的
public void dat2voAfter(CarDTO carDTO,@MappingTarget CarVO carVO){
List<PartDTO> partDTOS = carDTO.getPartDTOS();
//判断零件集合是否为空,给carVo设置值
boolean hasPart = partDTOS != null && !partDTOS.isEmpty();
carVO.setHasPart(hasPart);
}
测试如上,结果打印
测试如上,结果打印
CarVO(id=330, vin=vin12345698, price=123789.685, totalPrice=145698.67, publishDate=2022-08-04 14:11:11, brandName=品牌, hasPart=true, driverVO=DriverVO(driverId=2, fullName=哇哈), color=白色)
4.批量转换
抽象方法
public abstract List<CarVO> dtos2vos(List<CarDTO> carDTOS);
测试
@Test
public void test3() {
CarDTO carDTO = buildCarDTO();
List<CarDTO> carDTOList = new ArrayList<>();
carDTOList.add(carDTO); //source
List<CarVO> carVOS = CarConvert.INSTANCE.dtos2vos(carDTOList);
System.out.println(carVOS);
}
5. @BeanMappring
配置忽略mapstruct的默认映射行为,只映射了Mapping的属性
汽车VO对象
@Data
public class VehicleVO {
/**
* 编号
*/
private Long id;
/**
* 裸车的价格
*/
private Double price;
/**
* 品牌
*/
private String brandName;
}
抽象方法
@BeanMapping(ignoreByDefault = true)
@Mapping(source = "id",target = "id")
@Mapping(source = "brand",target = "brandName")
public abstract VehicleVO carDTO2vehicleVO(CarDTO carDTO);
测试类:
@Test
public void test4() {
CarDTO carDTO = buildCarDTO();
VehicleVO vehicleVO = CarConvert.INSTANCE.carDTO2vehicleVO(carDTO);
System.out.println(vehicleVO);
}
结果打印:
VehicleVO(id=330, price=null, brandName=品牌)
6.@InheritConfiguration避免同样配置写多份
抽象方法
1.借用之前抽象方法
@BeanMapping(ignoreByDefault = true)
@Mapping(source = "id",target = "id")
@Mapping(source = "brand",target = "brandName")
public abstract VehicleVO carDTO2vehicleVO(CarDTO carDTO);
2.利用@InheritConfiguration,继承配置,避免以上注解重复
@InheritConfiguration //避免同样的配置写多份
/*@BeanMapping(ignoreByDefault = true)
@Mapping(source = "id",target = "id")
@Mapping(source = "brand",target = "brandName")*/
@Mapping(target = "id",ignore = true)
public abstract VehicleVO updateVehicleVO(CarDTO carDTO,@MappingTarget VehicleVO vehicleVO);
测试类
@Test
public void test4() {
CarDTO carDTO = buildCarDTO();
VehicleVO vehicleVO = CarConvert.INSTANCE.carDTO2vehicleVO(carDTO);
System.out.println(vehicleVO);
CarDTO carDTO2 = new CarDTO();
//测试@InheritConfiguration继承配置
//通过carDTO2的属性值来更新已存在的vehicleVO对象
//brandName修改了,id被覆盖 VehicleVO(id=null, price=null, brandName=迈巴赫)
carDTO2.setBrand("迈巴赫");
VehicleVO updateVehicleVO = CarConvert.INSTANCE.updateVehicleVO(carDTO2, vehicleVO);
System.out.println(updateVehicleVO);
}
结果打印
VehicleVO(id=330, price=null, brandName=品牌)
VehicleVO(id=330, price=null, brandName=迈巴赫)
7.@InheritInverseConfiguration反向映射不需反过来再写一遍
只继承@Mapping注解,不继承@BeanMapring注解
抽象接口
@BeanMapping(ignoreByDefault = true)
@Mapping(source = "id",target = "id")
@Mapping(source = "brand",target = "brandName")
//VehicleVO(id=330, price=null, brandName=品牌)
public abstract VehicleVO carDTO2vehicleVO(CarDTO carDTO);
反向继承 name指定用哪个方法
@InheritInverseConfiguration(name = "carDTO2vehicleVO")
@BeanMapping(ignoreByDefault = true)
public abstract CarDTO vehicleVO2carDTO(VehicleVO vehicleVO);
测试类
@Test
public void test6() {
VehicleVO vehicleVO = new VehicleVO();
vehicleVO.setId(999L);
vehicleVO.setBrandName("别克");
vehicleVO.setPrice(65899544d);
CarDTO dto = CarConvert.INSTANCE.vehicleVO2carDTO(vehicleVO);
System.out.println(dto);
}
结果打印:
CarDTO(id=999, vin=null, price=0.0, totalPrice=0.0, publishDate=null, brand=别克, partDTOS=null, driverDTO=null, color=null)
8.与spring整合
抽象类的@Mapper注解加上spring
import org.mapstruct.Mapper;
@Mapper(componentModel = "spring")
public abstract class CarConvert {
//获取对象INSTANCE并使用
public static CarConvert INSTANCE = Mappers.getMapper(CarConvert.class);
//转换方法
public abstract CarVO dto2vo(CarDTO carDTO);
}