Sim*_*mon 9 java spring modelmapper spring-boot
TL; DR
我希望以一种从AbstractParent映射到AbstractParentDTO的方式使用modelMapper,稍后在ModelMapper-Config中调用每个Sub-class的特定映射器,然后跳过其余的(abstrac-class)映射.
怎么可能?这是正确的方法吗?有设计缺陷吗?
是)我有的:
父实体:
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn(name = "type")
public abstract class Parent {
//some more fields
}
Run Code Online (Sandbox Code Playgroud)
一个子实体:
//Basic Lombok Annotations
@DiscriminatorValue("child_a")
public class ChildA extends Parent {
//some more fields
}
Run Code Online (Sandbox Code Playgroud)
另一个子实体:
@DiscriminatorValue("child_b")
public class ChildB extends Parent {
//some more fields
}
Run Code Online (Sandbox Code Playgroud)
然后我有父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
}
Run Code Online (Sandbox Code Playgroud)
一个孩子DTO:
public class ClassADTO extends ParentDTO {
//some more fields
}
Run Code Online (Sandbox Code Playgroud)
和另一个DTO:
public class ClassBDTO extends ParentDTO {
//some more fields
}
Run Code Online (Sandbox Code Playgroud)
在我的情况下,我将从控制器获取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");
}
Run Code Online (Sandbox Code Playgroud)
只有我还有一些让孩子变得更笨重的孩子.
我想要的是:
而不是多次检查DTO(或实体)是什么实例,我只想写例如:
modelmapper.map(parent, ParentDTO.class)
Run Code Online (Sandbox Code Playgroud)
并执行"...的实例"检查我的ModelMapper配置中的ONCE.
我尝试过的:
我已经为我的ModelMapper配置中定义的每个可能的方向和映射大小写了不同的转换器(因为它们需要更复杂的映射).
我试图通过为Parent Classes编写一个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;
};
Run Code Online (Sandbox Code Playgroud)
和:
modelMapper.createTypeMap(Parent.class, ParentDTO.class)
.setPreConverter(parentParentDTOConverter);
Run Code Online (Sandbox Code Playgroud)
但我总是得到相同的MappingError:
1)无法实例化目标com.myexample.data.dto.ParentDTO的实例.确保com.myexample.data.dto.ParentDTOO具有非私有无参数构造函数.
我得到(我猜),我不能构造一个抽象类的对象.但那不是我正在尝试的,是吗?我想在完成PreConverter之后,modelMapper仍在完成Mapping的其余部分.我也尝试用.setConverter设置它,但总是有相同的结果.
有谁知道如何'禁用'自定义映射?我真的不想编写像映射器那样的"伪映射器",只是为每个场景调用特定的映射器.
我的设计很糟糕吗?你会如何改进它?
这只是没有实现到ModelMapper中吗?
任何帮助和提示表示赞赏.
我会使用 ObjectMapper 而不是 ModelMapper。
在父类中添加获取鉴别器值的可能性。
//..
public class Parent {
@Column(name = "type", insertable = false, updatable = false)
private String type;
//getters and setters
}
Run Code Online (Sandbox Code Playgroud)
您的 ParentDTO 应映射到 Child(*)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 {
// ..
}
Run Code Online (Sandbox Code Playgroud)
在转换服务/方法中添加一个忽略未知的对象映射器(以忽略您在 DTO 类中未声明的内容)
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
Run Code Online (Sandbox Code Playgroud)
只需简单地调用:
Parent parent = // get from repository
ParentDTO parentDTO = objectMapper.readValue(objectMapper.writeValueAsBytes(parent), ParentDTO.class);
Run Code Online (Sandbox Code Playgroud)
通过这种方式,您的 ParentDTO 始终使用正确的类型进行实例化。
好吧,我找到的解决方案使用转换器。在这种情况下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));
....
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2684 次 |
| 最近记录: |