如何正确实现多级继承多态对象的MapStruct映射
MapStruct 多态DTO与Entity双向映射配置
现有代码结构
DTO层定义
Store类结构:
public class Store { @JsonProperty("name") private String name; @JsonProperty("pets") @Valid private List<Pet> pets = new ArrayList<>(); // getters/setters }
Pet基类配置了Jackson多态规则:
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "type", visible = true) @JsonSubTypes({ @JsonSubTypes.Type(value = Cat.class, name = "cat"), @JsonSubTypes.Type(value = Dog.class, name = "dog"), @JsonSubTypes.Type(value = Pig.class, name = "pig"), }) public class Pet { public enum TypeEnum { CAT("cat"), DOG("dog"), PIG("pig"); } // 省略type字段、getter/setter }
Pet子类为空实现:
public class Cat extends Pet{} public class Dog extends Pet{} public class Pig extends Pet{}
初始Mapper代码(存在问题)
@Mapper public interface StoreEntityMapper { StoreEntity toStoreEntity(Store store); Store to Store(StoreEntity store); // 方法名存在空格笔误 } @Mapper public interface PetEntityMapper { PetEntity toPetEntity(Pet pet); Pet to Pet (PetEntity pet); // 方法名存在空格笔误 }
正确配置方案
前提:Entity层需与DTO层保持对应多态结构,即存在PetEntity基类,以及CatEntity、DogEntity、PigEntity三个子类,PetEntity中需存在type字段区分子类类型。
方案1:MapStruct 1.5+ 版本(推荐,原生支持多态映射)
使用@SubclassMapping注解显式声明所有子类的映射对应关系,MapStruct会自动识别基类引用指向的实际子类类型,完成双向转换。
首先修正Pet映射器:
@Mapper public interface PetEntityMapper { // 基类双向映射,声明所有子类对应关系 @SubclassMapping(source = Cat.class, target = CatEntity.class) @SubclassMapping(source = Dog.class, target = DogEntity.class) @SubclassMapping(source = Pig.class, target = PigEntity.class) @SubclassMapping(source = CatEntity.class, target = Cat.class) @SubclassMapping(source = DogEntity.class, target = Dog.class) @SubclassMapping(source = PigEntity.class, target = Pig.class) PetEntity toPetEntity(Pet pet); Pet toPet(PetEntity pet); // 子类具体映射方法,子类有专属字段时直接在方法上加@Mapping注解配置即可 CatEntity toCatEntity(Cat cat); Cat toCat(CatEntity catEntity); DogEntity toDogEntity(Dog dog); Dog toDog(DogEntity dogEntity); PigEntity toPigEntity(Pig pig); Pig toPig(PigEntity pigEntity); }
然后修正Store映射器,引入Pet映射器即可,MapStruct会自动处理List<Pet>集合的多态元素映射:
@Mapper(uses = PetEntityMapper.class) public interface StoreEntityMapper { StoreEntity toStoreEntity(Store store); Store toStore(StoreEntity store); }
方案2:MapStruct 1.5以下版本兼容方案
低版本无@SubclassMapping支持,通过自定义默认方法判断实例类型,结合@IterableMapping指定集合元素映射逻辑实现。
Pet映射器配置:
@Mapper public interface PetEntityMapper { @Named("toPetEntity") default PetEntity convertPetToEntity(Pet pet) { if (pet instanceof Cat) return toCatEntity((Cat) pet); if (pet instanceof Dog) return toDogEntity((Dog) pet); if (pet instanceof Pig) return toPigEntity((Pig) pet); return null; } @Named("toPet") default Pet convertEntityToPet(PetEntity entity) { if (entity instanceof CatEntity) return toCat((CatEntity) entity); if (entity instanceof DogEntity) return toDog((DogEntity) entity); if (entity instanceof PigEntity) return toPig((PigEntity) entity); return null; } // 子类具体映射方法 CatEntity toCatEntity(Cat cat); Cat toCat(CatEntity catEntity); DogEntity toDogEntity(Dog dog); Dog toDog(DogEntity dogEntity); PigEntity toPigEntity(Pig pig); Pig toPig(PigEntity pigEntity); }
Store映射器需要显式指定集合的映射规则:
@Mapper(uses = PetEntityMapper.class) public interface StoreEntityMapper { StoreEntity toStoreEntity(Store store); Store toStore(StoreEntity store); @IterableMapping(qualifiedByName = "toPetEntity") List<PetEntity> mapPetsToEntities(List<Pet> pets); @IterableMapping(qualifiedByName = "toPet") List<Pet> mapEntitiesToPets(List<PetEntity> petEntities); }
注意事项
- 原有Jackson多态配置与MapStruct映射逻辑完全独立,互不影响,不需要为了映射修改Jackson的序列化/反序列化规则。
- 初始代码中
to Store、to Pet的方法名存在多余空格,需要删除空格改为合法的方法名,否则MapStruct无法识别为映射方法。 - 如果子类存在专属字段,直接在对应子类的映射方法上添加
@Mapping注解配置字段映射规则即可。
内容的提问来源于stack exchange,提问作者Happy Coder
相关产品推荐
相关产品推荐

