SpringBoot分层概念澄清 DTO、BO、VO
spring boot项目中: DTO就是前端到控制层 BO就是数据库到服务层 VO就是服务层到前端 这样理解是否正确在Spring Boot项目中关于DTO、BO和VO的划分确实存在一些常见约定但你的理解中有些地方需要进一步澄清。下面是对这些概念在典型分层架构中的解释1. DTOData Transfer Object数据传输对象你的理解前端到控制层更准确的定位DTO 通常用于不同层之间的数据传输尤其是控制层Controller与服务层Service之间也可以用于服务层与外部接口之间。作用接收前端请求参数如JSON → DTO返回给前端的响应数据如DTO → JSON避免直接暴露实体Entity结构同时可以聚合多个字段或进行数据校验方向双向前端 → 控制层控制层 → 前端2. BOBusiness Object业务对象你的理解数据库到服务层更准确的定位BO 主要封装业务逻辑在服务层内部使用。它通常由一个或多个实体Entity组合而成也可能包含一些业务状态或方法用于处理复杂的业务规则。注意数据库到服务层的直接映射通常由实体Entity完成如JPA的Entity而不是BO。BO是服务层对实体进行业务加工后的对象。方向仅在服务层内部流转3. VOView Object视图对象你的理解服务层到前端更准确的定位VO 是专门为前端视图定制的数据对象由服务层返回给控制层再由控制层序列化后发送给前端。它通常包含前端需要展示的字段可能经过格式化、聚合或裁剪。与DTO的关系在很多项目中返回给前端的对象也常被称为DTO但VO更强调“视图展示”的语义而DTO更强调“数据传输”。如果严格区分VO可以看作是服务于前端视图的DTO。方向服务层 → 控制层 → 前端修正后的理解前端 → 控制层通常用DTO接收请求参数控制层 → 服务层可使用DTO传递参数服务层内部处理时可能转换为BO或直接操作Entity服务层内部使用BO处理业务逻辑可能组合多个Entity服务层 → 控制层返回VO或 DTO给前端展示数据库 ↔ 服务层通过Entity进行ORM映射Entity 代表数据持久化对象总结你的基本方向是对的但术语的精确职责和层次需要更清晰DTO是跨层传输的通用载体常用于前端与后端、服务层之间的交互。BO是服务层的业务抽象不直接与数据库或前端耦合。VO是专为前端视图定制的对象。Entity才是数据库到服务层的直接映射。在实际项目中团队可以根据自身约定灵活命名但保持分层清晰和数据隔离是关键。那如果使用MyBatis多表联合查询出来的数据集resultType不是用BO吗在 MyBatis 中进行多表联合查询时返回的结果集通常不应该直接定义为 BO业务对象而更常见的是使用DTO数据传输对象或专门为该查询创建的POJO例如命名为XxxQueryResult。下面详细解释原因和最佳实践。1. 为什么多表查询结果不适合直接用 BOBO 的本质业务对象BO是服务层内部对业务逻辑的封装它可能包含多个实体Entity的组合甚至包含业务方法。BO 的设计目的是为业务服务而不是为数据库查询服务。MyBatis 查询结果的本质多表联合查询返回的是数据的投影来自不同表的字段组合通常只是原始数据的简单聚合不包含业务行为。它应该是一个轻量的数据容器直接映射为 POJO。2. 应该使用什么对象DTO数据传输对象这是最常用的选择。DTO 用于在不同层次之间传输数据比如从数据库层MyBatis到服务层或者从服务层到控制层。多表查询的结果正是数据在不同表之间的传输用 DTO 非常合适。自定义查询结果对象你也可以为每个特定的多表查询定义一个 POJO放在类似dto或vo包下命名为XxxDetailDTO、XxxQueryResult等。这其实也是 DTO 的一种形式。3. 示例说明假设有user表和order表需要查询用户及其最近订单的信息sqlSELECT u.id, u.name, o.order_no, o.amount FROM user u LEFT JOIN order o ON u.id o.user_id WHERE ...对应的 MyBatis 结果映射javapublic class UserOrderDTO { private Long id; // 来自 user 表 private String name; // 来自 user 表 private String orderNo; // 来自 order 表 private BigDecimal amount; // 来自 order 表 // getters/setters }然后在 Mapper 中xmlselect idselectUserWithOrder resultTypecom.example.dto.UserOrderDTO ... /select这里的UserOrderDTO就是专为这个查询设计的 DTO它不属于任何实体Entity也不包含业务逻辑只负责承载查询结果。4. 与 BO 的关系如果服务层需要基于这个查询结果执行复杂的业务逻辑你可以在服务层将 DTO 转换为 BO让 BO 继续后续的业务处理。但 MyBatis 直接返回的仍是 DTO。例如java// Service 层 ListUserOrderDTO dtoList userMapper.selectUserWithOrder(...); ListUserOrderBO boList dtoList.stream().map(dto - { UserOrderBO bo new UserOrderBO(); // 可能补充其他业务字段或调用业务方法 bo.setUser(User.builder().id(dto.getId()).name(dto.getName()).build()); bo.setOrder(Order.builder().orderNo(dto.getOrderNo()).amount(dto.getAmount()).build()); return bo; }).collect(Collectors.toList());5. 结论MyBatis 多表查询的结果类型→DTO 或自定义查询结果对象放在 dto 包下。BO是服务层的业务抽象由服务层根据 DTO 和实体Entity组装而成不应该由 MyBatis 直接返回。这样做可以保持各层职责清晰数据库层只负责数据获取服务层负责业务组装控制层负责视图适配。代码示例服务层Entity、BO、DTOsrc/main/java/com/weiyu/service/WorkflowService.java/** * 查询办理信息列表 */ public ListAuditInfoDTO queryAuditInfoList(Integer workflowInstanceId) { ListAuditInfoJoinQueryDTO queryDTOs workflowMapper.selectAuditInfoListByJoinQuery(workflowInstanceId); // Entity - BO预加载所有相关数据避免每次遍历都查询数据库 ActivityInstanceAuditContextBO auditStateDescBO new ActivityInstanceAuditContextBO(); ListActivityInstance activityInstances getActivityInstancesByWorkflowInstanceId(workflowInstanceId); ListAuditInfo auditInfos getAuditInfosByWorkflowInstanceId(workflowInstanceId); ListAuditLog auditLogs getAuditLogsByWorkflowInstanceId(workflowInstanceId); auditStateDescBO.setActivityInstances(activityInstances); auditStateDescBO.setAuditInfos(auditInfos); auditStateDescBO.setAuditLogs(auditLogs); ListAuditInfoDTO auditInfoDTOs new ArrayList(); for (AuditInfoJoinQueryDTO queryDTO : queryDTOs) { AuditInfoDTO auditInfoDTO new AuditInfoDTO(); BeanUtils.copyProperties(queryDTO, auditInfoDTO); // 计算办理状态 String auditStateDesc getActivityInstanceAuditStateDesc( auditStateDescBO, queryDTO.activityId(), queryDTO.auditor() ); auditInfoDTO.setAuditStateDesc(auditStateDesc); auditInfoDTOs.add(auditInfoDTO); } return auditInfoDTOs; }控制层DTO、VOsrc/main/java/com/weiyu/controller/WorkflowController.java/** * 查询办理信息列表 * * param workflowInstanceId 流程实例id * return {link Result}lt;{link List}lt;{link AuditInfoVO}gt;gt; */ GetMapping(/audit-info-list/{workflowInstanceId}) public ResultListAuditInfoVO queryAuditInfoList(PathVariable Min(1) Integer workflowInstanceId) { String endpoint /workflow/audit-info-list/; String method queryAuditInfoList; log.info(【工作流程】查询办理信息列表{}{}{}, endpoint, workflowInstanceId, method); ListAuditInfoDTO auditInfoDTOs workflowService.queryAuditInfoList(workflowInstanceId); // DTO - VO ListAuditInfoVO auditInfoVOs auditInfoDTOs.stream() .map(dto - { AuditInfoVO vo new AuditInfoVO(); BeanUtils.copyProperties(dto, vo); return vo; }) .toList(); return Result.success(auditInfoVOs); }
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2409246.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!