【问题标题】:Java: Mapping DTOs hierarchy using MapStructJava:使用 MapStruct 映射 DTO 层次结构
【发布时间】:2020-01-14 05:08:01
【问题描述】:

我有 Pet、Dog 和 Cat 实体类。 Dog 和 Cat 类扩展了 Pet。

我还有用@JsonSubtype 注释的 PetDTO、DogDTO 和 CatDTO,所以 Jackson 可以很好地解析 dto 的类。

我想使用 MapStruct 编写一个映射器,它采用 PetDTO 实体(可以是 DogDTO 或 CatDTO)并返回 Dog 或 Cat。

在这种情况下,对我来说,使用映射库的主要目标是避免使用 instanceof 的糟糕代码。

有什么想法吗?谢谢!

【问题讨论】:

  • I want to write a mapper using MapStruct that takes a PetDTO entity (can be a DogDTO or a CatDTO) and returns a Dog or a Cat. - 你如何想象这样一个函数的签名?
  • 公共宠物地图(PetDTO){ ... }
  • 为什么不将它们映射到单独的映射方法中呢?这背后是否有一些特定的业务需求?
  • 控制器层中的端点接收 PetDTO。由于@JsonSubtype 中的元数据,Jackson 知道将其映射到 DogDTO 或 CatDTO。假设我在变量“petDTO”中有它。然后我必须将它映射到调用 Pet pet = mapper.map(petDTO) 的域模型中,所以它必须是一个方法。

标签: mapstruct mapper


【解决方案1】:

目前不可能开箱即用 - 请在 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);
}

【讨论】:

    【解决方案2】:

    对于上述类似情况,我最终实现 Mapper 的方式是使用开关类型、MapStruct Update Existing 和创建 Mapper 的组合。

    在我的例子中,源对象的一个​​属性决定了我们必须生成的子类。 我最初对每个子类型都有不同的映射器,但是公共映射属性的重复似乎是错误的。所以我想出了以下方法,利用 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);
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-10-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-09-14
      • 2020-07-07
      • 2023-03-23
      相关资源
      最近更新 更多