ModelMapper:基于子类选择映射

时间:2018-09-24 08:32:28

标签: java spring spring-boot modelmapper

TL; DR

我想以某种方式使用modelMapper,将其从AbstractParent映射到AbstractParentDTO,然后在ModelMapper-Config中为每个子类调用特定的映射器,然后跳过其余的(抽象类)映射。

那怎么可能?这是正确的方法吗?有设计缺陷吗?


我所拥有的:

父实体:

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

一个子实体:

//Basic Lombok Annotations
@DiscriminatorValue("child_a")
public class ChildA extends Parent {
//some more fields
}

另一个子实体:

@DiscriminatorValue("child_b")
public class ChildB extends Parent {
//some more fields   
}

然后我有父DTO类:

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

一个孩子的DTO:

public class ClassADTO extends ParentDTO {
//some more fields
}

和另一个DTO:

public class ClassBDTO extends ParentDTO {
//some more fields
}

就我而言,我将从控制器获取DTO,并在将其提供给服务时将其映射到实体。我必须在5-6个端点中做同样的事情。

端点大致如下:

@PreAuthorize(CAN_WRITE)
@PutMapping("/{id}")
public ResponseEntity<ParentDTO> update(
        @PathVariable("id") UUID id,
        @RequestBody @Valid ParentDTO parentDTO) {

    Parent parent = parentService.update(id, parentDTO);

    if (parentDTO instanceof ChildADTO) {
        return ResponseEntity.ok(modelMapper.map(parent, ChildADTO.class));
    } else if (parentDTO instanceof ChildBDTO) {
        return ResponseEntity.ok(modelMapper.map(parent, ChildBDTO.class));
    }
    throw new BadRequestException("The Parent is not Valid");
}

只有我还有几个孩子使事情变得更笨重。


我想要的:

我不需要写很多次DTO(或实体)实例,而只是想写例如:

modelmapper.map(parent, ParentDTO.class)

并在我的ModelMapper配置中执行“实例...”检查一次。


我尝试过的事情:

对于我在ModelMapper配置中定义的每个可能的方向和映射案例,我已经有不同的Converter(因为它们仍然需要更复杂的映射)。

我试图通过为父类再编写一个Converter并将其设置为ModelMapper PreConverter来解决问题:

    //from Entity to DTO
    Converter<Parent, ParentDTO> parentParentDTOConverter = mappingContext -> {
        Parent source = mappingContext.getSource();
        ParentDTO dest = mappingContext.getDestination();

        if (source instanceof CHildA) {
            return modelMapper.map(dest, ChildADTO.class);
        } else if (source instanceof ChildB) {
            return modelMapper.map(dest, ChildBDTO.class);
        } 
        return null;
    };

和:

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

但是我总是遇到相同的MappingError:

  

1)无法实例化目的地实例   com.myexample.data.dto.ParentDTO。确保这件事   com.myexample.data.dto.ParentDTOO具有非私有无参数   构造函数。

我得到的

(我猜是),我无法构造抽象类的Object。但这不是我要尝试的,是吗? 我猜想在完成我的PreConverter之后,modelMapper仍在进行其余的映射。我也尝试过使用.setConverter进行设置,但始终具有相同的结果。


  • 有人知道如何“禁用”自定义映射吗?我不 真的很想写像映射器一样的“伪映射器” 为每种情况调用特定的映射器。

  • 我的设计不好吗?您将如何改进它?

  • 这是否尚未在ModelMapper中实现?

感谢您的帮助和提示。

3 个答案:

答案 0 :(得分:4)

我将使用ObjectMapper而不是ModelMapper。

父级类中,添加了获得鉴别值的可能性。

configuration.getPane().setSize(95, Unit.PERCENTAGE);

您的ParentDTO应该映射到Child(*)DTO

//..
public class Parent {

    @Column(name = "type", insertable = false, updatable = false)
    private String type;
    //getters and setters
}

在转换服务/方法中,添加一个对象忽略未知的映射器(忽略您在DTO类中未声明的内容)

@JsonTypeInfo(
        use = JsonTypeInfo.Id.NAME,
        include = JsonTypeInfo.As.PROPERTY,
        property = "type")
@JsonSubTypes({
        @JsonSubTypes.Type(value = ChildADTO.class, name = "child_a"),
        @JsonSubTypes.Type(value = ChildBDTO.class, name = "child_b")
})
 public abstract class ParentDTO {
   // ..
 }

只需调用:

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

这样,您的ParentDTO总是用正确的类型实例化。

答案 1 :(得分:1)

好吧,我找到的解决方案使用转换器。在这种情况下,modelMapper不会尝试创建抽象类的新实例,而是直接使用转换器。

您可以将所有转换器放在同一位置

modelMapper.createTypeMap(ChildA.class, ParentDTO.class)
            .setConverter(mappingContext -> modelMapper.map(mappingContext.getSource(), ClassADTO.class));

modelMapper.createTypeMap(ChildB.class, ParentDTO.class)
            .setConverter(mappingContext -> modelMapper.map(mappingContext.getSource(), ClassBDTO.class));
....

答案 2 :(得分:0)

怎么样

select *
from Admission a
where a.paperlessCommunication = 'Y' and
      not exists (select 1 from EmailAddress ea where ea.emailaddress = a.emailaddress);

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