目錄
前言:
1:如何使用:
1:引入mapStruct和lombok依賴
2:增加轉換器(@Mapper)
3:寫一個轉換方法
4:擷取對象INSTANCE并使用
2:幾個使用的關鍵點:
@Mapper
@Mappings和@Mapping(指定屬性之間的映射關系)
@AfterMapping 和 @MappingTarget (屬性的自定義映射處理)
@BeanMapping(ignoreByDefault 忽略mapstruct預設的映射行為)
@InheritConfiguration
@InheritInverseConfiguration
3:與spring 結合使用
4:舉例子:DTO轉VO
前言:
在我們進行bean類屬性的拷貝的時候,經常使用org.springframework.beans.BeanUtils這個工具類;
當然可能還有apache的BeanUtils;
還有你可以自己寫get,set方法來進行屬性複制拷貝(代碼較多,複雜);
-
BeanUtils缺點:
1:利用反射的機制,性能較差;若是對象的屬性超級多的話,這個缺點會被無線的放大;
2:需要【類型】和【名稱】都一樣才會進行映射,不同名字的還需要自己手動get/set指派;
如果我們需要實作簡單的bean拷貝,選擇spring的bean拷貝功能是個不錯選擇;
當業務量不大時,不管選擇哪個架構都沒什麼問題,隻要功能支援就ok了;
但是當資料量大的時候,可能就需要考慮性能問題了;
mapstruct:
1:開發會浪費時間,因為要寫轉化器類(需要聲明bean的轉換接口);
2:在添加新的字段的時候也要進行方法的修改,很麻煩;
3:但是優點也是明顯的:不需要進行反射,編譯後的代碼其實就是我們經常寫的get/set方法,性能是很高的(性能媲美直接的get/set);
1:如何使用:
1:引入mapStruct和lombok依賴
<org.mapstruct.version>1.3.1.Final</org.mapstruct.version>
<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>
<scope>provided</scope>
</dependency>
2:增加轉換器(@Mapper)
建立一個抽象類,或者接口,标注:@Mapper
注意這個Mappeer引入的是:
import org.mapstruct.Mapper;
3:寫一個轉換方法
方法名字是可以任意的,沒有要求
一般常用 dtoToVo 或者其他的;
4:擷取對象INSTANCE并使用
CarConvert INSTANCE = Mappers.getMapper(CarConvert.class);
2:幾個使用的關鍵點:
@Mapper
- 預設映射規則
- 【同類型且同名】的屬性,會自動映射;
- mapstruct會自動進行類型轉換:
- 8種基本類型 和 對應的包裝類型 之間
- 8中基本類型(包括他們的包裝類型)和 string 之間
- 日期類型和 string 之間
@Mappings和@Mapping(指定屬性之間的映射關系)
- 用于指定屬性之間的映射關系:
- 日期格式化:dateFormat="yyyy-MM-dd HH:mm:ss"
- 數字格式化:numberFormat = "#.00"
-
@Mapping(source = "totalPrice", target = "totalPrice", numberFormat = "#.00") @Mapping(source = "createDate", target = "createDate", dateFormat = "yyyy-MM-dd HH:mm:ss")
- source 或者 target 多餘的屬性,對方沒有,不會報錯;
- ignore:用于配置忽略某一個屬性,不想轉換傳回就配置ignore;
-
@Mapping(target = "color", ignore = true)
-
- 不同名字的屬性,也是可以映射的;
-
@Mapping(source = "name", target = "carName")
-
- 屬性是引用對象的映射:
- 就是說如果一個屬性是引用類型的話:預設是沒有進行複制的;
- 對象也可以映射的,需要加下面這個配置
-
@Mapping(source = "carDetailDTO", target = "carDetailVO")
- 這個時候,mapstruct會自動去查找,代碼中有沒有把CarDetailDTO 轉為 CarDetailVO的方法;
- 是以:我們還需要寫這兩個對象的轉換方法;
- 就是說如果一個屬性是引用類型的話:預設是沒有進行複制的;
- 批量映射:List<CarDTO>---->List<CarVO>
@AfterMapping 和 @MappingTarget (屬性的自定義映射處理)
用于針對mapstruct 處理不了的一些屬性的映射;
在映射的最後一步,對屬性的自定義映射處理;
@BeanMapping(ignoreByDefault 忽略mapstruct預設的映射行為)
- ignoreByDefault:忽略mapstruct的預設映射行為;隻映射那些配置了@Mapping的屬性
- 避免不需要的指派,避免屬性覆寫;
- 就是可能我某一個屬性,不需要你mapstruct給我映射過來;
@InheritConfiguration
中文解釋:用于繼承配置
- 可用于更新的場景,避免同樣的配置寫多份
@InheritInverseConfiguration
中文解釋:反向繼承配置
- 反向映射,不用反過來再寫一遍
-
@InheritConfiguration(name = "")
- name 指定使用哪一個方法的配置(方法就是你轉換類中寫的那些轉換方法)寫方法的名字
- 注意:這裡隻繼承@Mapping注解配置,不會繼承@BeanMapping
3:與spring 結合使用
@Autowired
private CarConvert carConvert;
這樣就可以直接使用這個對象的方法:
carConvert.toCarVo()
這個時候調用上面這個方法是有問題的,
需要在這個類上,跟spring關聯起來:
@Mapper(componentModel = "spring")
public interface CarConvert {
}
實質就是給生成的類加了@Component注解;
那麼這個時候我們原來寫的這句話就不需要了:CarConvert INSTANCE = Mappers.getMapper(CarConvert.class);
直接用【對象.方法】的形式就可以調用方法了;
4:舉例子:DTO轉VO
先建立一些輔助類:
@Data
public class CarDTO {
private Long id;
private Double totalPrice;
private String createDate;
private String color;
private String name;
private CarDetailDTO carDetailDTO;
private List<PartDTO> partDTOList;
}
@Data
public class CarVO {
private Long id;
private Double totalPrice;
private Date createDate;
private String color;
private String carName;
private CarDetailVO carDetailVO;
private List<PartVo> partVoList;
// 判斷partDTOList是否有值
private Boolean hasPart;
}
@Data
public class CarDetailDTO {
private Integer id;
private String code;
}
@Data
public class CarDetailVO {
private Integer carDetailId;
private String carDetailCode;
}
@Data
public class PartDTO {
private String attribute;
private String attributeName;
}
@Data
public class PartVo {
private String attribute;
private String attributeName;
}
/**
* 寶馬車
*/
@Data
public class BaoMaVO {
private Long id;
private Double totalPrice;
private String brandName;
}
建立一個轉換器:
@Mapper(componentModel = "spring")
public interface CarConvert {
// 如果用了上面那個 @Mapper(componentModel = "spring") ,就是跟spring 整合了,其實就不需要用這中方式調用,這行代碼就可以注釋掉
CarConvert INSTANCE = Mappers.getMapper(CarConvert.class);
@Mappings({
@Mapping(source = "totalPrice", target = "totalPrice", numberFormat = "#.00"),
@Mapping(source = "createDate", target = "createDate", dateFormat = "yyyy-MM-dd HH:mm:ss"),
@Mapping(target = "color", ignore = true),
@Mapping(source = "name", target = "carName"),
@Mapping(source = "carDetailDTO", target = "carDetailVO") // 引用對象轉換
})
CarVO toCarVO(CarDTO carDTO);
/**
* 這個方法也可以用于上面那個引用對象轉換
*/
@Mapping(source = "id", target = "carDetailId")
@Mapping(source = "code", target = "carDetailCode")
CarDetailVO carDetailDTOToCarDetailVo(CarDetailDTO carDetailDTO);
/**
* 自定義屬性的映射
*
* @AfterMapping :表示讓mapstruct在調用完成自動轉換的方法之後,會來自動調用本方法
* @MappingTarget :表示傳來的carVO對象是已經指派過的
*/
@AfterMapping
public default void carDTOToVOAfter(CarDTO carDTO,@MappingTarget CarVO carVO){
if (!CollectionUtils.isEmpty(carDTO.getPartDTOList())){
carVO.setHasPart(true);
}
}
/**
* 集合批量轉換(就是上面這個方法的批量轉換-toCarVO)
*/
List<CarVO> toCarVOList(List<CarDTO> carDTOList);
/**
* 忽略mapstruct 的預設的映射行為
*
* 下面這個ignore 就是忽略了 totalPrice 屬性的映射,不讓這個屬性值被映射過來;
* 但是若是一個類中我們需要忽略的屬性有幾十個,可能要寫幾十行這個@Mapping(ignore=),非常不友善
*
* 是以可以通過@BeanMapping 來進行配置
*/
@Mapping(source = "totalPrice", target = "totalPrice", ignore = true)
BaoMaVO toBaoMaVO(CarDTO carDTO);
/**
* 配置忽略mapstruct的預設映射行為,隻映射那些配置了@Mapping的屬性
*/
@BeanMapping(ignoreByDefault = true)
@Mapping(source = "id", target = "id") // 這裡隻映射id屬性
@Mapping(source = "name", target = "brandName") // 這裡還映射name屬性
BaoMaVO toBaoMaVO2(CarDTO carDTO);
/**
* 更新場景:繼承配置,避免同樣的配置寫多份
*/
@InheritConfiguration
void updateBaoMaVo(CarDTO carDTO,@MappingTarget BaoMaVO baoMaVO);
}
測試類:
public static void main(String[] args) {
//批量轉換(List<CarDTO>---->List<CarVO>)
List<CarDTO> carDTOList = Lists.newArrayList(); //source 假設這裡已經有資料了
List<CarVO> carVOList = Lists.newArrayList(); // target
// 以前是用for循環周遊轉換
carDTOList.forEach(carDTO -> {
CarVO carVO = CarConvert.INSTANCE.toCarVO(carDTO);
carVOList.add(carVO);
});
// mapstruce 專門給我們提供了一個方法,用于轉換list的
List<CarVO> carVOListNew = CarConvert.INSTANCE.toCarVOList(carDTOList);
// @InheritConfiguration 繼承配置
CarDTO carDTO = new CarDTO();
//carDTO.setXXX
//car.set...
BaoMaVO baoMaVO = CarConvert.INSTANCE.toBaoMaVO2(carDTO);
System.out.println(baoMaVO); // baoMaVO 沒有修改以前的值
// 希望通過 carDTO2 中的屬性值,來更新 baoMaVO 中的屬性值
CarDTO carDTO2 = new CarDTO();
//carDTO2.setXXX
CarConvert.INSTANCE.updateBaoMaVo(carDTO2, baoMaVO); // 進行應該
System.out.println(baoMaVO); // baoMaVO 修改以後的值
}
其實最常用的就兩個:
@Mapping
@Mappings
其他都不太常用,因為我們主要是做bean屬性拷貝的,如果需要拷貝後重新複制,也可以用上面的,但是一般都直接在拷貝後自己寫set方法了,這樣便于閱讀;
https://lux-sun.blog.csdn.net/article/details/113946112
https://blog.csdn.net/zhige_me/article/details/80699784