Java:使用 MapStruct 映射 DTO 层次结构
Java: Mapping DTOs hierarchy using MapStruct
我有宠物、狗和猫实体 class。狗和猫 class 扩展了 Pet.
我还有 PetDTO、DogDTO 和 CatDTO 注释 @JsonSubtype 所以 Jackson 很好地解析了 dtos 的 class。
我想使用 MapStruct 编写一个映射器,它采用 PetDTO 实体(可以是 DogDTO 或 CatDTO)和 returns 狗或猫。
在这种情况下,对我来说,使用映射库的主要目标是避免使用 instanceof 的糟糕代码。
有什么想法吗?谢谢!
目前无法开箱即用 - 请在 mapstruct 的 GitHub 中查看此票证:#366 Support for abstract class mapping or classes with base class。您可以尝试将其推送到那里,或者自己贡献此功能。看起来是一个合理的功能要求。
我想在目前的情况下,这是你最好的选择:
@Mapper
public interface PetMapper {
default PetDTO toPetDto(Pet pet) {
if (pet instanceof Dog) {
return toDogDTO((Dog) pet);
}
if (pet instanceof Cat) {
return toCatDTO((Cat) pet);
}
throw new IllegalArgumentException("Unknown subtype of Pet");
}
default Pet toPetEntity(PetDTO petDTO) {
if (petDTO instanceof DogDTO) {
return toDogEntity((DogDTO) petDTO);
}
if (petDTO instanceof CatDTO) {
return toCatEntity((CatDTO) petDTO);
}
throw new IllegalArgumentException("Unknown subtype of PetDTO");
}
DogDTO toDogDTO(Dog dog);
Dog toDogEntity(DogDTO dogDTO);
CatDTO toCatDTO(Cat cat);
Cat toCatEntity(CatDTO catDTO);
}
我最终为上述类似情况实现映射器的方式是结合使用开关类型、MapStruct 更新现有映射器和创建映射器。
在我的例子中,源对象上的 属性 指示了我们必须生成的子类。
我最初为每个子类型使用了不同的映射器,但公共映射属性的重复似乎是错误的。所以我想到了以下内容,利用 MapStruct 的能力来使用更新映射器来处理常见的父类型属性:
import org.mapstruct.*;
@Mapper
@Named("QualifierPetMapper")
public interface PetMapper {
@Named("DelegatingPetMapper")
@BeanMapping(ignoreByDefault = true)
default PetTarget mapPet(PetSource petSource) {
switch (petSource.getPetType()) {
case "DOG":
DogTarget dogTarget = mapDog(petSource);
updatePet(dogTarget, petSource);
return (dogTarget);
case "CAT":
CatTarget catTarget = mapCat(petSource);
updatePet(catTarget, petSource);
return (catTarget);
default:
throw new CustomException("Unsupported Pet type: "+ petSource.getPetType());
}
}
@BeanMapping(ignoreByDefault = true)
// Specific mappings for Dog
@Mapping(target = "dogfood.name", source = "dogfoodName")
DogTarget mapDog(PetSource petSource);
@BeanMapping(ignoreByDefault = true)
// Specific mappings for Cat
@Mapping(target = "fish.name", source = "favoriteFish")
CatTarget mapCat(PetSource petSource);
@Named("RootPetMapper")
@BeanMapping(ignoreByDefault = true)
// Common properties for Pet
@Mapping(target = "weight.value", source = "weightValue")
@Mapping(target = "name.value", source = "petName")
@Mapping(target = "color", source = "mainColor")
void updatePet(@MappingTarget PetTarget petTarget, PetSource petSource);
}
我有宠物、狗和猫实体 class。狗和猫 class 扩展了 Pet.
我还有 PetDTO、DogDTO 和 CatDTO 注释 @JsonSubtype 所以 Jackson 很好地解析了 dtos 的 class。
我想使用 MapStruct 编写一个映射器,它采用 PetDTO 实体(可以是 DogDTO 或 CatDTO)和 returns 狗或猫。
在这种情况下,对我来说,使用映射库的主要目标是避免使用 instanceof 的糟糕代码。
有什么想法吗?谢谢!
目前无法开箱即用 - 请在 mapstruct 的 GitHub 中查看此票证:#366 Support for abstract class mapping or classes with base class。您可以尝试将其推送到那里,或者自己贡献此功能。看起来是一个合理的功能要求。
我想在目前的情况下,这是你最好的选择:
@Mapper
public interface PetMapper {
default PetDTO toPetDto(Pet pet) {
if (pet instanceof Dog) {
return toDogDTO((Dog) pet);
}
if (pet instanceof Cat) {
return toCatDTO((Cat) pet);
}
throw new IllegalArgumentException("Unknown subtype of Pet");
}
default Pet toPetEntity(PetDTO petDTO) {
if (petDTO instanceof DogDTO) {
return toDogEntity((DogDTO) petDTO);
}
if (petDTO instanceof CatDTO) {
return toCatEntity((CatDTO) petDTO);
}
throw new IllegalArgumentException("Unknown subtype of PetDTO");
}
DogDTO toDogDTO(Dog dog);
Dog toDogEntity(DogDTO dogDTO);
CatDTO toCatDTO(Cat cat);
Cat toCatEntity(CatDTO catDTO);
}
我最终为上述类似情况实现映射器的方式是结合使用开关类型、MapStruct 更新现有映射器和创建映射器。
在我的例子中,源对象上的 属性 指示了我们必须生成的子类。 我最初为每个子类型使用了不同的映射器,但公共映射属性的重复似乎是错误的。所以我想到了以下内容,利用 MapStruct 的能力来使用更新映射器来处理常见的父类型属性:
import org.mapstruct.*;
@Mapper
@Named("QualifierPetMapper")
public interface PetMapper {
@Named("DelegatingPetMapper")
@BeanMapping(ignoreByDefault = true)
default PetTarget mapPet(PetSource petSource) {
switch (petSource.getPetType()) {
case "DOG":
DogTarget dogTarget = mapDog(petSource);
updatePet(dogTarget, petSource);
return (dogTarget);
case "CAT":
CatTarget catTarget = mapCat(petSource);
updatePet(catTarget, petSource);
return (catTarget);
default:
throw new CustomException("Unsupported Pet type: "+ petSource.getPetType());
}
}
@BeanMapping(ignoreByDefault = true)
// Specific mappings for Dog
@Mapping(target = "dogfood.name", source = "dogfoodName")
DogTarget mapDog(PetSource petSource);
@BeanMapping(ignoreByDefault = true)
// Specific mappings for Cat
@Mapping(target = "fish.name", source = "favoriteFish")
CatTarget mapCat(PetSource petSource);
@Named("RootPetMapper")
@BeanMapping(ignoreByDefault = true)
// Common properties for Pet
@Mapping(target = "weight.value", source = "weightValue")
@Mapping(target = "name.value", source = "petName")
@Mapping(target = "color", source = "mainColor")
void updatePet(@MappingTarget PetTarget petTarget, PetSource petSource);
}