spring ModelMapper:基于Child类选择Map

x33g5p2x  于 2024-01-05  发布在  Spring
关注(0)|答案(4)|浏览(171)

TL;DR

我想以一种从AbstractParentMap到AbstractParentDTO的方式使用modelMapper,然后在ModelMapper-Config中为每个子类调用特定的Map器,然后跳过其余的(抽象类)Map。
这怎么可能?这是正确的方法吗?有设计缺陷吗?

我拥有的:

父实体:

  1. @Inheritance(strategy = InheritanceType.JOINED)
  2. @DiscriminatorColumn(name = "type")
  3. public abstract class Parent {
  4. //some more fields
  5. }

字符串
一个子实体:

  1. //Basic Lombok Annotations
  2. @DiscriminatorValue("child_a")
  3. public class ChildA extends Parent {
  4. //some more fields
  5. }


另一个子实体:

  1. @DiscriminatorValue("child_b")
  2. public class ChildB extends Parent {
  3. //some more fields
  4. }


然后我有父DTO类:

  1. @JsonTypeInfo(use = JsonTypeInfo.Id.NAME)
  2. @JsonSubTypes({
  3. @JsonSubTypes.Type(value = ChildA.class, name = "child_a"),
  4. @JsonSubTypes.Type(value = ChildB.class, name = "child_b"),
  5. public abstract class ParentDTO {
  6. //some more fields
  7. }


一个孩子DTO:

  1. public class ClassADTO extends ParentDTO {
  2. //some more fields
  3. }


另一个DTO:

  1. public class ClassBDTO extends ParentDTO {
  2. //some more fields
  3. }


在我的例子中,我将从控制器获得DTO,并在将它们提供给服务时将它们Map到实体。
端点大致如下所示:

  1. @PreAuthorize(CAN_WRITE)
  2. @PutMapping("/{id}")
  3. public ResponseEntity<ParentDTO> update(
  4. @PathVariable("id") UUID id,
  5. @RequestBody @Valid ParentDTO parentDTO) {
  6. Parent parent = parentService.update(id, parentDTO);
  7. if (parentDTO instanceof ChildADTO) {
  8. return ResponseEntity.ok(modelMapper.map(parent, ChildADTO.class));
  9. } else if (parentDTO instanceof ChildBDTO) {
  10. return ResponseEntity.ok(modelMapper.map(parent, ChildBDTO.class));
  11. }
  12. throw new BadRequestException("The Parent is not Valid");
  13. }


只不过我多了几个查尔兹,让事情变得更庞大。

我想要的:

而不是检查一堆的时候什么示例的DTO(或实体),我只是想写例如:

  1. modelmapper.map(parent, ParentDTO.class)


并在我的ModelMapper配置中检查一次“instance of.”。

我尝试过的:

我已经为每一个可能的方向和在ModelMapper配置中定义的Map情况使用了不同的转换器(因为它们需要更复杂的Map)。
为了解决这个问题,我为Parent Classes再写了一个Converter,并将其设置为ModelMapper PreConverter:

  1. //from Entity to DTO
  2. Converter<Parent, ParentDTO> parentParentDTOConverter = mappingContext -> {
  3. Parent source = mappingContext.getSource();
  4. ParentDTO dest = mappingContext.getDestination();
  5. if (source instanceof CHildA) {
  6. return modelMapper.map(dest, ChildADTO.class);
  7. } else if (source instanceof ChildB) {
  8. return modelMapper.map(dest, ChildBDTO.class);
  9. }
  10. return null;
  11. };


以及:

  1. modelMapper.createTypeMap(Parent.class, ParentDTO.class)
  2. .setPreConverter(parentParentDTOConverter);


但我总是得到相同的MappingError:
1)未能示例化目标com. myexample. data. dto. ParentDTO的示例。请确保com.myexample.data.dto.ParentDTOO具有非私有无参数构造函数。
我得到了(我猜),我不能构造一个抽象类的Object。但这不是我正在尝试的,是吗?我猜modelMapper在完成我的PreConverter后仍然在做剩下的Mapping。我也尝试过用.setConverter设置它,但总是得到相同的结果。

  • 有谁知道如何"禁用"自定义Map吗?我真的不想写“伪Map器”,它像Map器一样工作,只是为每个场景调用特定的Map器。
  • 我的设计很糟糕吗?你会如何改进它?
  • 这是不是还没有在ModelMapper中实现?

任何帮助和提示是赞赏。

odopli94

odopli941#

我找到的解决方案是使用转换器。在这种情况下,modelMapper不会尝试创建抽象类的新示例,而是直接使用转换器。
你可以把所有的转换器在同一个地方

  1. modelMapper.createTypeMap(ChildA.class, ParentDTO.class)
  2. .setConverter(mappingContext -> modelMapper.map(mappingContext.getSource(), ClassADTO.class));
  3. modelMapper.createTypeMap(ChildB.class, ParentDTO.class)
  4. .setConverter(mappingContext -> modelMapper.map(mappingContext.getSource(), ClassBDTO.class));
  5. ....

字符串

ndasle7k

ndasle7k2#

我会使用ObjectMapper而不是ModelMapper。
Parent类中添加获取函数值的可能性。

  1. //..
  2. public class Parent {
  3. @Column(name = "type", insertable = false, updatable = false)
  4. private String type;
  5. //getters and setters
  6. }

字符串
您的ParentDTO应Map到Child(*)DTO

  1. @JsonTypeInfo(
  2. use = JsonTypeInfo.Id.NAME,
  3. include = JsonTypeInfo.As.PROPERTY,
  4. property = "type")
  5. @JsonSubTypes({
  6. @JsonSubTypes.Type(value = ChildADTO.class, name = "child_a"),
  7. @JsonSubTypes.Type(value = ChildBDTO.class, name = "child_b")
  8. })
  9. public abstract class ParentDTO {
  10. // ..
  11. }


在转换服务/方法中添加一个带有ignore unknown的对象Map器(忽略未在DTO类中声明的内容)

  1. ObjectMapper objectMapper = new ObjectMapper();
  2. objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);


只需简单地调用:

  1. Parent parent = // get from repository
  2. ParentDTO parentDTO = objectMapper.readValue(objectMapper.writeValueAsBytes(parent), ParentDTO.class);


通过这种方式,您的ParentDTO始终使用正确的类型进行示例化。

展开查看全部
u4vypkhs

u4vypkhs3#

怎么样

  1. TypeMap<Parent.class, ParentDTO.class> typeMap = modelMapper.createTypeMap(Parent.class, ParentDTO.class);
  2. typeMap
  3. .include(ChildA .class, ClassADTO .class)
  4. .include(ChildB.class, ClassbDTO.class);

字符串
参考:http://modelmapper.org/user-manual/type-map-inheritance

xu3bshqb

xu3bshqb4#

我已经创建了一个转换器,它使用Jackson注解进行继承,所以如果您正在使用Jackson并且有很多注解,您就不需要创建两次Map(一次用于Jackson,一次用于ModelMapper)。

  1. public class JacksonInheritanceConverter<S, D> implements Converter<S, D> {
  2. @SneakyThrows({IntrospectionException.class, IllegalAccessException.class, InvocationTargetException.class})
  3. public D convert(MappingContext<S, D> context) {
  4. var destinationType = context.getDestinationType();
  5. var jsonTypeInfo = destinationType.getAnnotation(JsonTypeInfo.class);
  6. var discriminator = new PropertyDescriptor(jsonTypeInfo.property(), context.getSourceType()).getReadMethod().invoke(context.getSource());
  7. var subclassType = Arrays.stream(destinationType.getAnnotation(JsonSubTypes.class).value())
  8. .filter(type -> type.name().equals(discriminator.toString()))
  9. .findFirst()
  10. .orElseThrow(() -> new IllegalArgumentException("Source: " + context.getSourceType().getSimpleName() + " Discriminator: " + discriminator))
  11. .value();
  12. var finalMappingContext = context.create(context.getSource(), subclassType);
  13. var result = context.getMappingEngine().map(finalMappingContext);
  14. return context.getDestinationType().cast(result);
  15. }
  16. }

字符串
但是我找不到一种方法来把它分配给所有的Map,当它看到@JsonTypeInfo时,它会自动触发。所以仍然需要手动检查基于继承的类的列表。

  1. modelMapper.createTypeMap(ChildA.class, ParentDTO.class)
  2. .setConverter(new JacksonInheritanceConverter<>());

展开查看全部

相关问题