【发布时间】:2020-08-04 07:30:54
【问题描述】:
我试图避免在调用不同的 EndPoint 时使用 DTO 反模式,其中每个都返回同一实体的不同表示。当我在 Rest EndPoint 中返回实体时,我想利用 Jackson 执行的序列化。这意味着序列化只进行一次,而不是使用 DTO(实体到 DTO 和 DTO 到 Json):
端点示例:
@GetMapping("/events")
public ResponseEntity<List<Event>> getAllEvents(){
try {
List<Event> events = (List<Event>) eventsRepository.findAll();
return new ResponseEntity<List<Event>>(
events, HttpStatus.OK);
}catch(IllegalArgumentException e) {
return new ResponseEntity<List<Event>>(HttpStatus.BAD_REQUEST);
}
}
@GetMapping("/events/{code}")
public ResponseEntity<Event> retrieveEvent(@PathVariable String code){
Optional<Event> event = eventsRepository.findByCode(code);
return event.isPresent() ?
new ResponseEntity<Event>(event.get(), HttpStatus.OK) :
new ResponseEntity<Event>(HttpStatus.BAD_REQUEST);
}
Serializer(扩展 StdSerializer 的类):
@Override
public void serialize(Event value, JsonGenerator gen,
SerializerProvider provider) throws IOException {
if(firstRepresentation) {
//First Representation
gen.writeStartObject();
gen.writeNumberField("id", value.getId());
gen.writeObjectField("creation", value.getCreation());
gen.writeObjectFieldStart("event_tracks");
for (EventTrack eventTrack : value.getEventsTracks()) {
gen.writeNumberField("id", eventTrack.getId());
gen.writeObjectField("startTime", eventTrack.getStartTime());
gen.writeObjectField("endTime", eventTrack.getEndTime());
gen.writeNumberField("priority", eventTrack.getPriority());
gen.writeObjectFieldStart("user");
gen.writeNumberField("id", eventTrack.getUser().getId());
gen.writeEndObject();
gen.writeObjectFieldStart("state");
gen.writeNumberField("id", eventTrack.getState().getId());
gen.writeStringField("name", eventTrack.getState().getName());
gen.writeEndObject();
}
gen.writeEndObject();
gen.writeEndObject();
}else if(secondRepresentation) {
//Second Representation
}
}
实体:
@JsonSerialize(using = EventSerializer.class)
@RequiredArgsConstructor
@Getter
@Setter
public class Event implements Comparable<Event>{
private Long id;
@JsonIgnore
private String code;
private Timestamp creation;
@NonNull
private String description;
@JsonUnwrapped
@NonNull
private EventSource eventSource;
@NonNull
private String title;
@NonNull
private Category category;
@NonNull
@JsonProperty("event_tracks")
private List<EventTrack> eventsTracks;
@JsonProperty("protocol_tracks")
private List<ProtocolTrack> protocolTracks;
public void addEventTrack(@NonNull EventTrack eventTracks) {
eventsTracks.add(eventTracks);
}
@JsonIgnore
public EventTrack getLastEventTrack() {
return eventsTracks.get(eventsTracks.size() - 1);
}
@JsonIgnore
public int getLastPriority() {
return getLastEventTrack().getPriority();
}
public void generateUUIDCode() {
this.code = UUID.randomUUID().toString();
}
@Override
public int compareTo(Event o) {
return this.getLastPriority() - o.getLastPriority();
}
}
所以,到目前为止,我已经能够使用扩展 StdDeserializer 的类来序列化表示类型,但这并不能让我灵活地以多种方式扩展相同实体属性的表示。虽然我已经尝试过使用 Json 注释,但我意识到实体类具有的表示越多,它可能会变得非常复杂,它应该很简单。也许有些想法我该怎么做。
谢谢。
【问题讨论】:
-
为什么你认为 DTO 是一种反模式?您可以直接从查询中创建 DTO。这是一个非常好的模式。
-
并且实体到 DTO 的转换,除非它们在不同的机器上运行不是
serialization.. 它只是conversion.. 在同一个 jvm 上仍然是 java 对象到另一个 java 对象。 -
@SimonMartinell 我想是因为我重复信息的程度几乎可以完全重复实体类。为什么不使用基类并使用负责的类对其进行转换以直接对其进行序列化,Json Serializaer?
-
不要只考虑将实体转换为 DTOS。更喜欢直接从数据库加载好的 dto ......这将避免资源浪费,因为在这里你总是加载完整的实体,然后使用序列化程序过滤一些字段......这是浪费......
-
@CodeScale 这是一个非常好的观点,我喜欢它。虽然用基础实体也带来或请求必要的信息,这满足抽象设计原则。所以我一直在寻找一个单独负责转换 Json 对应表示的 Jackson 类。现在,我会听从你的建议。
标签: spring spring-boot jackson jsonserializer