当前位置: 首页 > 知识库问答 >
问题:

ModelMapper:选择基于子类的映射

韦昊焜
2023-03-14

TL; DR

我想以一种从AbstractP的映射方式来使用ModelMapper,然后在ModelMapper-Config中为每个子类调用特定的映射器,然后跳过其余的(abstrac类)映射。

这怎么可能?这是正确的做法吗?是否存在设计缺陷?

我所拥有的:

母公司:

@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个endpoint做同样的事情。

endpoint大致如下所示:

@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配置中定义的每个可能的方向和映射案例提供了不同的转换器(因为它们无论如何都需要更复杂的映射)。

我试图通过为父类编写一个转换器并将其设置为ModelMapper预转换器来解决我的问题:

    //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);

但我总是遇到同样的映射错误:

1) 未能实例化目标com的实例。我举个例子。数据dto。父母。确保com。我举个例子。数据dto。ParentDTOO有一个非私有的无参数构造函数。

我得到(我猜),我不能构造抽象类的对象。但那不是我想要的,是吗?我猜modelMapper在完成我的预转换后仍在进行其余的映射。我也试着把它设置为。设置转换器,但始终具有相同的结果。

>

  • 有人知道如何禁用自定义映射吗?我真的不想写“伪映射器”,它们像映射器一样,只是为每个场景调用特定的映射器。

    我的设计很糟糕吗?你将如何改进它?

    这还没有实现到ModelMapper中吗?

    任何帮助和暗示都将不胜感激。

  • 共有3个答案

    潘安平
    2023-03-14

    怎么样

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

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

    丁学
    2023-03-14

    嗯,我找到的解决方案使用转换器。在本例中,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));
    ....
    
    华哲茂
    2023-03-14

    在父类中添加获取鉴别器值的可能性。

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

    您的ParentDTO应映射到子(*)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 {
       // ..
     }
    

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

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

    只需简单地调用:

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

    通过这种方式,您的父母DTO总是使用正确的类型实例化。

     类似资料:
    • 我有一个C#类 产品有名称、价格等属性 我想要一个linq查询,它将返回带有产品数组的产品类别的结构,但仅当产品的属性等于一个值时。例如价格是10的地方。 我试过了 但这返回给我一个产品数组,但我想要产品类别中所有符合条件的产品。 作为一个额外的挑战,我也有多个标准,所以在这个例子中,假设价格等于10,30和50。其中一个标准有两个值,这就增加了复杂性。我要做的只是调用不同的选择标准,然后在结尾处

    • 我有一个样式规则,当一个标记有两个类时,我想应用于它。在没有JavaScript的情况下,有什么方法可以执行此操作吗?换句话说: 只有在同时应用了和类时,我才要应用我的样式规则。

    • 让我们假设我有一个这样的结构化数组: 我将这个结构称为“categories”,所以,我在这个数组中有六个类别。我的目标是根据一个类别随机挑选一个产品。 我想做一个基于速率的类别选择,据我所知,我必须计算这个类别在数组中代表多少百分比,例如: 这会给我类似的东西: 好的,现在我要做一个简单的算法,根据这些比率得到类别;我想我现在需要在范围之间选择一个随机数,并制作一些“切片”,例如: 如果随机数介

    • 我有这个: 我想选择类为col-md-4,6,8,12的排班的孩子。有没有什么好办法一次全选? 可以有多个行DIV,我将使用wrapAll将每个行DIV的子级包装到其他DIV中。所以如果我使用wrapAll,它只是将其他行div的所有内容转移到第一个行div。如果我将选择器查询放在循环内部,它只是继续包装子项乘以行divs的数量。我不想让这种事发生。 你们听懂了吗?

    • 我与ModelMapper框架有麻烦。请解释为什么我看到以下行为。 我在build.gradle有以下依赖性 和一个类客户: 我还有一个地图绘制工具: 还有一个测试 在fred()中,方法输出是非红色的“Customer{name=fred,age=40}”(“Customer{name=null,age=40}”)。你能解释一下为什么吗?为什么我在第一个方法中看不到输出“George”?