【发布时间】:2021-11-11 20:17:06
【问题描述】:
型号
我们使用org.modelmapper 进行 bean 映射。这是数据模型(为简洁起见,使用public):
class Root {
public String foo;
public List<Element> elements;
}
class Element {
public String foo; // <- this is the field that should be replicated from RootDTO
public String bar;
}
class RootDTO {
public String foo;
public List<ElementDTO> elements;
}
class ElementDTO {
public String bar; // notice how there is no `foo` attribute in the DTO
}
预期映射(示例)
输入:
RootDTO
|-- foo = "foo"
|-- elements
|--ElementDTO(bar="bar")
|--ElementDTO(bar="something")
|--ElementDTO(bar="else")
输出:
Root
|-- foo = "foo"
|-- elements
|--Element(foo="foo", bar="bar")
|--Element(foo="foo", bar="something")
|--Element(foo="foo", bar="else")
问题
如您所见,如果不是 Element.foo 字段应该从 RootDTO.foo 获取其值,ModelMapper 将非常轻松地处理这种情况。
如何配置ModelMapper 来实现我的目标?
我想要解决方案:
- 如果要在模型中引入新的匹配属性,则不需要开发人员更改映射的代码。
- 通过对
ModelMapper的单个方法调用来完成。
当前配置
myModelMapper.typeMap(ElementDTO.class, Element.class).implicitMappings();
myModelMapper.typeMap(RootDTO.class, Root.class).implicitMappings();
使用该配置时的结果
Root mappedRoot = myModelMapper.map(rootDto, Root.class);
assertEquals(rootDto.foo, mappedRoot.foo);
assertEquals(rootDto.elements.size(), mappedRoot.elements.size());
// let's assume we have 1 Element
assertEquals(rootDto.elements.get(0).bar, mappedRoot.elements.get(0).bar);
assertEquals(rootDto.foo, mappedRoot.elements.get(0).foo); // FAILS! But this is what I want
// indeed, the value of `mappedRoot.elements.get(0).foo` is `null` because it was not mapped
探索的途径
想法 1
在我看来,如果我可以设置 order,我可以简单地这样配置它:
Converter<RootDTO, Root> replicateFooValue = new Converter<>() {
@Override
public Root convert(MappingContext<RootDTO, Root> context) {
final String valueToReplicate = context.getSource().foo;
for (Element elem : context.getDestination().elements) {
elem.foo = valueToReplicate;
}
return context.getDestination();
}
};
myModelMapper.typeMap(ElementDTO.class, Element.class).implicitMappings();
myModelMapper.typeMap(RootDTO.class, Root.class).implicitMappings()
.thenUseConverter(replicateFooValue);
...但我认为这是不可能的。
想法 2
如果我可以在Converter<> 本身中使用ModelMapper 的映射功能,那么我可以在尝试简单地设置我想要的值之前使用implicitMappings(),但再一次:我不认为这是可能的。
想法 3
确保它有效的一种方法是:
Converter<RootDTO, Root> myCustomConverter = new Converter<>() {
@Override
public Root convert(MappingContext<RootDTO, Root> context) {
final Root mappedRoot = new Root();
// map each field individually, by hand
return mappedRoot;
}
};
myModelMapper.addConverter(myCustomConverter);
...但是如果我要在我的数据模型中添加新字段,它需要维护。
想法 4
添加一个Converter<RootDTO, Element>,它只会填充foo 值。这将导致以下用法:
Root mappedRoot = myModelMapper.map(rootDto, Root.class);
mappedRoot.elements.forEach(e -> myModelMapper.map(rootDto, e));
这并不理想,因为整个预期行为并未封装在单个调用中:开发人员必须知道(并记住)进行第二次调用才能实现所需的映射。
想法 5
创建一个包含Idea 4 所示逻辑的实用程序类。
这也是一个坏主意,因为它要求开发人员了解(并记住)他们为什么需要以这种方式进行特定映射,而不是使用 ModelMapper。
【问题讨论】:
标签: java mapping modelmapper