java - ModelMapper: Choose mapping based on Child class
问题描述
TL;DR
I want to use modelMapper in a way that I map from AbstractParent to AbstractParentDTO and later in the ModelMapper-Config call the specific mappers for each Sub-class and then skip the rest of the (abstrac-class) mappings.
How is that Possible? Is this the right approach? Is there a design flaw?
What I have:
The parent entity:
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn(name = "type")
public abstract class Parent {
//some more fields
}
One child entity:
//Basic Lombok Annotations
@DiscriminatorValue("child_a")
public class ChildA extends Parent {
//some more fields
}
Another child entity:
@DiscriminatorValue("child_b")
public class ChildB extends Parent {
//some more fields
}
Then I have the parent DTO class:
@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
}
One Child DTO:
public class ClassADTO extends ParentDTO {
//some more fields
}
and another DTO:
public class ClassBDTO extends ParentDTO {
//some more fields
}
In my case I'll get DTO's from the controller and map them to Entities when giving them to the Service. I'll have to do the same thing in 5-6 Endpoints.
The Endpoints look roughly like this:
@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");
}
Only that I have a few more Childs that make things even bulkier.
What I want:
Instead of checking a bunch of times what instance the DTO (or Entity) is, I simply want to write for example:
modelmapper.map(parent, ParentDTO.class)
and do the "instance of..." check ONCE in my ModelMapper Configuration.
What I've tried:
I already have different Converters for every possible direction and mapping-case defined in my ModelMapper Configuration (since they require more complex mapping anyways).
I've tried to solve my problem by writing one more Converter for the Parent Classes and setting it as a 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;
};
and:
modelMapper.createTypeMap(Parent.class, ParentDTO.class)
.setPreConverter(parentParentDTOConverter);
But I'm always getting the same MappingError:
1) Failed to instantiate instance of destination com.myexample.data.dto.ParentDTO. Ensure that com.myexample.data.dto.ParentDTOO has a non-private no-argument constructor.
which I get (I guess), I cannot construct an Object of an abstract class. But thats not what I'm trying, am I? I guess that modelMapper is still doing the rest of the Mapping after finishing with my PreConverter. I've also tried to set it with .setConverter but always with the same result.
Does anyone knows how to 'disable' the custom mappings? I don't really want to write "pseudo-mappers" that act like mappers and just call the specific mappers for each scenario.
Is my design just bad? How would you improve it?
Is this just not implemented into ModelMapper yet?
Any help and hint is appreciated.
解决方案
Well, the solution I found uses converters. In this case modelMapper
doesn't try to create a new instance of abstract class, but uses the converter directly.
You can put all the converters in same place
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));
....
推荐阅读
- python - 计算空间距离返回操作数错误
- javascript - datetimepicker startDate endDate 控件
- javascript - ASP.NET MVC将div中span的innerHTML文本传递给控制器
- oauth-2.0 - OpenID Connect:可以从应用程序启动网页并自动登录用户吗?
- git - GIT:如何合并先前从合并提交中删除的更改
- css - 调整窗口大小时,多级推送菜单对浏览器来说太重了
- vb.net - 设置断点
- ubuntu-14.04 - Devstack 安装:运行 ./stack.sh 后 n-cpu 和 q-l3 失败
- uml - UML 2.5.1 和 UML 2.5 之间的区别
- typescript - Typescript:如何声明代表 Treeview 的类型?可能吗?