如何通過 MapStruct 使用條件映射
一、簡介
MapStruct 是一個代碼生成工具,可簡化 Java bean 類型之間的映射。在本文中,我們將探討如何將條件映射與 MapStruct 結合使用,並研究不同的配置來實現它。
2. 使用 MapStruct 進行條件映射
當在對象之間映射數據時,我們經常發現需要根據某些條件映射屬性,MapStruct 提供了一些配置選項來實現此目的。
讓我們檢查一下需要基於幾個條件映射屬性的目標License
對象的實例:
public class License {
private UUID id;
private OffsetDateTime startDate;
private OffsetDateTime endDate;
private boolean active;
private boolean renewalRequired;
// getters and setters
}
輸入LicenseDto
包含可選的startDate
和endDate
:
public class LicenseDto {
private UUID id;
private LocalDateTime startDate;
private LocalDateTime endDate;
// getters and setters
}
LicenseDto
到License
的映射規則為:
-
startDate
– 如果輸入LicenseDto
請求中沒有startDate
,則將startDate
設置為當前日期 -
endDate
– 如果輸入LicenseDto
在請求中沒有endDate
,我們將endDate
設置為從當前日期算起的一年 -
active
– 如果endDate
是將來的時間,我們將其設置為true
-
renewalRequired
– 如果endDate
在接下來的兩週內,我們會將其設置為true
讓我們探討一下使用 MapStruct 提供的配置來實現此目的的一些方法。
2.1.使用表達式
MapStruct 提供了在映射表達式內使用任何有效 Java 表達式來生成映射的功能。讓我們利用此功能來映射startDate
:
@Mapping(target = "startDate", expression = "java(mapStartDate(licenseDto))")
License toLicense(LicenseDto licenseDto);
我們現在可以定義方法mapStartDate
:
default OffsetDateTime mapStartDate(LicenseDto licenseDto) {
return licenseDto.getStartDate() != null ?
licenseDto.getStartDate().atOffset(ZoneOffset.UTC) : OffsetDateTime.now();
}
編譯時,MapStruct 生成代碼:
@Override
public License toLicense(LicenseDto licenseDto) {
License license = new License();
license.setStartDate( mapStartDate(licenseDto) );
// Rest of generated mapping...
return license;
}
或者,如果不使用此方法,則可以將方法內的三元運算直接傳遞到expression
中,因為它是有效的 Java 表達式。
2.2.使用條件表達式
與表達式類似, 條件表達式是 MapStruct 中的一項功能,它允許基於字符串內的條件表達式將屬性映射為 Java 代碼。生成的代碼包含if
塊內的條件,因此,讓我們利用此功能來映射License
中的renewalRequired
:
@Mapping(target = "renewalRequired", conditionExpression = "java(isEndDateInTwoWeeks(licenseDto))", source = ".")
License toLicense(LicenseDto licenseDto);
我們可以在java()
方法中傳入任何有效的 Java 布爾表達式。讓我們在映射器接口中定義isEndDateInTwoWeeks
方法:
default boolean isEndDateInTwoWeeks(LicenseDto licenseDto) {
return licenseDto.getEndDate() != null
&& Duration.between(licenseDto.getEndDate(), LocalDateTime.now()).toDays() <= 14;
}
編譯時,如果滿足此條件,MapStruct 會生成代碼來設置renewalRequired
:
@Override
public License toLicense(LicenseDto licenseDto) {
License license = new License();
if ( isEndDateInTwoWeeks(licenseDto) ) {
license.setRenewalRequired( isEndDateInTwoWeeks( licenseDto ) );
}
// Rest of generated mapping...
return license;
}
當條件匹配時,還可以從源設置屬性的值。在這種情況下,映射器將使用源中的相應值填充所需的屬性。
2.3.使用映射前/映射後
在某些情況下,如果我們想通過自定義在映射之前或之後修改對象,我們可以使用 MapStruct 中的@BeforeMapping
和@AfterMapping
註解。讓我們使用此功能來映射endDate
:
@Mapping(target = "endDate", ignore = true)
License toLicense(LicenseDto licenseDto);
我們可以定義AfterMapping
註釋來有條件地映射endDate
。這樣我們就可以根據具體情況來控制映射:
@AfterMapping
default void afterMapping(LicenseDto licenseDto, @MappingTarget License license) {
OffsetDateTime endDate = licenseDto.getEndDate() != null ? licenseDto.getEndDate()
.atOffset(ZoneOffset.UTC) : OffsetDateTime.now()
.plusYears(1);
license.setEndDate(endDate);
}
我們需要將輸入LicenseDto
和目標License
對像作為參數傳遞給此afterMapping
方法。因此,這確保了 MapStruct 在返回License
對象之前生成調用此方法的代碼作為映射的最後一步:
@Override
public License toLicense(LicenseDto licenseDto) {
License license = new License();
// Rest of generated mapping...
afterMapping( licenseDto, license );
return license;
}
或者,我們可以使用BeforeMapping
註釋來實現相同的結果。
三、結論
在本文中,我們探索了使用 MapStruct 有條件地在 Java bean 類型之間映射屬性的不同方法。
與往常一樣,示例的源代碼可在 GitHub 上獲取。