使用DTO开发时能否返回真实实体?更新操作返回值该如何处理?
DTO更新操作返回方案及行业实践
1. 是否需要单独创建返回用DTO
非常建议拆分入参更新DTO和返回DTO,这是符合分层设计的常规操作,两者的职责完全不同:
- 更新用的入参DTO,仅需要承载客户端允许修改的字段即可,未修改字段为NULL属于正常情况,还可以针对更新场景单独做参数校验(比如主键ID必填、部分字段不可修改等)
- 返回用DTO需要承载持久化后的完整业务数据,包括服务端自动生成/更新的字段(比如更新时间、数据版本号、自动计算的统计字段、关联数据冗余值等),这些都不会出现在入参DTO中
如果你实在不想维护两套DTO类,也可以用同一个类,但是绝对不能直接返回客户端传入的DTO对象,必须将更新后保存的Entity对象转为DTO后再返回,避免出现非预期的NULL字段。
2. 更新操作返回void是不是行业通用做法
不存在统一的通用做法,完全根据业务场景决定:
- 内部微服务间调用、不需要感知更新后结果、对性能要求极高的场景,可以返回void,减少序列化和网络传输开销
- 对外提供的业务接口、前端需要拿到最新数据状态的场景,必须返回更新后的DTO,避免前端再发起一次查询请求,减少不必要的接口交互
参考实现伪代码
// 更新入参DTO:仅包含允许客户端修改的字段 public class EntityAUpdateReq { @NotNull(message = "更新对象ID不能为空") private Long id; private String name; private Integer age; } // 返回DTO:包含所有需要对外暴露的字段 public class EntityAResp { private Long id; private String name; private Integer age; private LocalDateTime updateTime; private Integer version; } // Service层核心逻辑 public EntityAResp updateEntityA(EntityAUpdateReq req) { // 1. 查询原有实体 EntityA originEntity = dao.getById(req.getId()); // 2. 用入参更新实体字段 if (req.getName() != null) { originEntity.setName(req.getName()); } if (req.getAge() != null) { originEntity.setAge(req.getAge()); } // 3. 填充服务端自动更新字段 originEntity.setUpdateTime(LocalDateTime.now()); originEntity.setVersion(originEntity.getVersion() + 1); // 4. 保存更新后的实体 EntityA savedEntity = dao.saveAndFlush(originEntity); // 5. 转换为返回DTO后返回 return convertToResp(savedEntity); }
不要为了减少类的数量强行复用入参DTO做返回,一方面可能导致敏感字段意外暴露,另一方面如果返回NULL字段会增加前端的解析成本,也不符合接口单一职责的设计原则。
内容的提问来源于stack exchange,提问作者user15599360
相关产品推荐
相关产品推荐

