MapStruct 不复制子类属性

MapStruct does not copy subclass properties

我有以下结构。我的子类的属性没有被复制到我的 DTO。

@Entity
@Inheritance(strategy = InheritanceType.JOINED)
class BaseClass
{
 private Integer baseProperty1;
 private Integer baseProperty2;
 //getters & setters
}


class SubClass extends BaseClass
{
 private Integer subProperty1;
 private Integer subProperty2;
 //getters & setters
}

class BaseSubDTO
{
 private Integer baseProperty1;
 private Integer baseProperty2;
 private Integer subProperty1;
 private Integer subProperty2;
 //getters & setters
}

class BaseClassService
{
 public BaseClass find()
 {
  return baseClassRepository.findById(101);
 }
}

class BaseClassController
{
 public BaseSubDTO find()
 {
  return mapper.toDTO(baseClassService.find());
 }
}

@Mapper(componentModel = "spring")
public interface DTOMapper
{
    BaseSubDTO toDTO(final BaseClass entity);
}

行:

return mapper.toDTO(baseClassService.find());

在控制器中没有将子类属性 subProperty1subProperty2 映射到我的 BaseSubDTO

如何将子类属性映射到 DTO 中?

mapstruct 似乎还不支持 Downcast 映射。参见 Support for Type-Refinement mapping (or Downcast Mapping)

为了保持通用性,您可以实现一个自定义映射器来检查传入对象的类型:

@Mapper
public interface ToDTOMapper {
    ToDTOMapper MAPPER = Mappers.getMapper(ToDTOMapper.class);

    BaseSubDTO toDTOFromBaseClass(BaseClass baseClass);

    BaseSubDTO toDTOFromSubClass(SubClass baseClass);

    default BaseSubDTO map(BaseClass baseClass) {
        if(baseClass instanceof SubClass) {
            return toDTOFromSubClass((SubClass)baseClass);
        } 
        return toDTOFromBaseClass(baseClass);
    }
}

用法:

SubClass subClass = ...
BaseClass baseClass = ...

ToDTOMapper mapper = ToDTOMapper.MAPPER;
BaseSubDTO dto = mapper.map(subClass);
dto = mapper.map(baseClass);

希望对您有所帮助。

这里的问题是映射器方法的实际类型用于确定映射;不考虑子类型。即,只有确切输入类型 (BaseClass) 上的属性映射到 BaseSubDTO,而不是任何可能在子类型上的字段(例如 SubClass)。

当您意识到映射是在编译时根据 class 的类型完成的,而不是在运行时基于反射完成时,这就更有意义了。特别要注意的是,在编译时无法知道对于给定的超级 class 存在或将存在哪些可能的子类型,因此 MapStruct 不可能在编译时提供此功能。

话虽这么说,即将推出的 MapStruct 1.5.0 提供了一个 @SubclassMapping 注释,允许指定映射方法处理的已知子 classes。如果传入一个已知的子类型,将委托一个更具体的映射方法。

这可以应用于您的特定示例,如下所示:

@Mapper(componentModel = "spring")
public interface DTOMapper {
    @SubclassMapping(source = SubClass.class, target = BaseSubDTO.class)
    @Mapping(target = "subProperty1", ignore = true)
    @Mapping(target = "subProperty2", ignore = true)
    BaseSubDTO toDTO(final BaseClass entity);

    BaseSubDTO toSubclassDTO(final SubClass entity);
}