使用新的@SubclassMapping 注释时如何忽略 MapStruct 中的某些字段?
How to ignore some fields in MapStruct when using the new @SubclassMapping annotation?
初始情况
使用当前 1.5.0.Beta2 MapStruct 版本 JDK 13.
领域模型
class Wrapper {
private Fruit fruit;
}
abstract class Fruit {
private int weight;
/* ... */
}
class Apple extends Fruit {
/* ... */
}
class Banana extends Fruit {
/* ... */
}
(省略相应的1:1 DTO)
映射器
包装器的映射器class
@Mapper(uses = {FruitMapper.class})
public interface WrapperMapper {
WrapperDto map(Wrapper wrapper)
}
水果映射器 class(es)
MapStruct Mapper
for abstract Fruit
class with method signature and annotations:
@Mapper(subclassExhaustiveStrategy = SubclassExhaustiveStrategy.RUNTIME_EXCEPTION /*...*/)
public interface FruitMapper {
@SubclassMapping(source = Apple.class, target = AppleDto.class)
@SubclassMapping(source = Banana.class, target = BananaDto.class)
FruitDto map(Fruit fruit);
}
问题
以上工作正常,直到需要在引用的摘要 class 上忽略一个字段(例如 Fruit
的 weight
)。
将此注释放入 WrapperMapper
map
方法...
@Mapping(target = "fruit.weight", ignore = true)
WrapperDto map(Wrapper wrapper)
...导致 The return type FruitDto is an abstract class or interface. Provide a non abstract / non interface result type or a factory method.
编译错误。
问题
有没有办法按照概述跳过 MapStruct 映射中的字段而不会出现此编译错误?
原因
发生的事情是因为你在WrapperMapper上指定它不会直接使用FruitMapper。相反,WrapperMapper 实现将生成一个新的映射方法来映射水果。 MapStruct 在这次尝试中失败并给你编译错误。
解决方案
如果您将忽略添加到 FruitMapper,它将继承它到两个子类映射:
@SubclassMapping( source = Apple.class, target = AppleDto.class )
@SubclassMapping( source = Banana.class, target = BananaDto.class )
@Mapping( target = "weight", ignore = true )
FruitDto map( Fruit fruit );
如果您想要一个有权重的映射和一个没有权重的映射,您可以将 @org.mapstruct.Named
注释添加到没有权重的映射,然后在 WrappedMapper 中使用它。
像这样:
@Mapper(subclassExhaustiveStrategy = SubclassExhaustiveStrategy.RUNTIME_EXCEPTION /*...*/)
public interface FruitMapper {
@SubclassMapping( source = Apple.class, target = AppleDto.class )
@SubclassMapping( source = Banana.class, target = BananaDto.class )
FruitDto map( Fruit fruit );
@SubclassMapping( source = Apple.class, target = AppleDto.class )
@SubclassMapping( source = Banana.class, target = BananaDto.class )
@Mapping( target = "weight", ignore = true )
@Named( "NoWeights" )
FruitDto map( Fruit fruit );
}
@Mapper(uses = {FruitMapper.class})
public interface WrapperMapper {
@Mapping( target = "fruit", qualifiedByName = "NoWeights" )
WrapperDto map(Wrapper wrapper)
}
初始情况
使用当前 1.5.0.Beta2 MapStruct 版本 JDK 13.
领域模型
class Wrapper {
private Fruit fruit;
}
abstract class Fruit {
private int weight;
/* ... */
}
class Apple extends Fruit {
/* ... */
}
class Banana extends Fruit {
/* ... */
}
(省略相应的1:1 DTO)
映射器
包装器的映射器class
@Mapper(uses = {FruitMapper.class})
public interface WrapperMapper {
WrapperDto map(Wrapper wrapper)
}
水果映射器 class(es)
MapStruct Mapper
for abstract Fruit
class with method signature and annotations:
@Mapper(subclassExhaustiveStrategy = SubclassExhaustiveStrategy.RUNTIME_EXCEPTION /*...*/)
public interface FruitMapper {
@SubclassMapping(source = Apple.class, target = AppleDto.class)
@SubclassMapping(source = Banana.class, target = BananaDto.class)
FruitDto map(Fruit fruit);
}
问题
以上工作正常,直到需要在引用的摘要 class 上忽略一个字段(例如 Fruit
的 weight
)。
将此注释放入 WrapperMapper
map
方法...
@Mapping(target = "fruit.weight", ignore = true)
WrapperDto map(Wrapper wrapper)
...导致 The return type FruitDto is an abstract class or interface. Provide a non abstract / non interface result type or a factory method.
编译错误。
问题
有没有办法按照概述跳过 MapStruct 映射中的字段而不会出现此编译错误?
原因
发生的事情是因为你在WrapperMapper上指定它不会直接使用FruitMapper。相反,WrapperMapper 实现将生成一个新的映射方法来映射水果。 MapStruct 在这次尝试中失败并给你编译错误。
解决方案
如果您将忽略添加到 FruitMapper,它将继承它到两个子类映射:
@SubclassMapping( source = Apple.class, target = AppleDto.class )
@SubclassMapping( source = Banana.class, target = BananaDto.class )
@Mapping( target = "weight", ignore = true )
FruitDto map( Fruit fruit );
如果您想要一个有权重的映射和一个没有权重的映射,您可以将 @org.mapstruct.Named
注释添加到没有权重的映射,然后在 WrappedMapper 中使用它。
像这样:
@Mapper(subclassExhaustiveStrategy = SubclassExhaustiveStrategy.RUNTIME_EXCEPTION /*...*/)
public interface FruitMapper {
@SubclassMapping( source = Apple.class, target = AppleDto.class )
@SubclassMapping( source = Banana.class, target = BananaDto.class )
FruitDto map( Fruit fruit );
@SubclassMapping( source = Apple.class, target = AppleDto.class )
@SubclassMapping( source = Banana.class, target = BananaDto.class )
@Mapping( target = "weight", ignore = true )
@Named( "NoWeights" )
FruitDto map( Fruit fruit );
}
@Mapper(uses = {FruitMapper.class})
public interface WrapperMapper {
@Mapping( target = "fruit", qualifiedByName = "NoWeights" )
WrapperDto map(Wrapper wrapper)
}