java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Java DTO和VO区别

Java中DTO和VO的区别举例详解

作者:趁你还年轻_

在Java开发中,VO和DTO都是用于数据传输的对象,但它们的含义和用途有所不同,这篇文章主要介绍了Java中DTO和VO区别的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下

前言

DTO(Data Transfer Object)和 VO(Value Object / View Object)是 Java 开发中用于不同层次间数据传递的对象,但它们的设计目的和使用场景有本质区别。

一、概念定义

DTO(Data Transfer Object)

VO(Value Object / View Object)

二、核心区别对比表

对比维度DTO (Data Transfer Object)VO (View Object)
设计目的减少网络调用,批量传输数据适配前端展示需求
数据范围可能包含多个聚合的数据针对单个页面/组件定制
数据敏感度可包含内部字段必须隐藏敏感字段(密码、金额等)
使用层次Service ↔ Controller、微服务间调用Controller ↔ 前端
字段格式化通常是原始数据类型包含格式化后的数据(如日期、金额显示格式)
变更频率相对稳定随前端需求频繁变化

三、详细说明与代码示例

场景:用户订单详情查询

数据库实体(DO - Data Object)

@Entity
@Table(name = "t_user")
public class UserDO {
    private Long id;
    private String username;
    private String password; // 敏感字段
    private String email;
    private String phone;
    private LocalDateTime createTime;
    private LocalDateTime updateTime;
    private Integer status; // 0-注销 1-正常 2-冻结
}

DTO(Service 层返回)

@Data
public class UserOrderDTO {
    // 用户基本信息
    private Long userId;
    private String username;
    private String email; // 可能包含敏感信息
    
    // 订单列表(跨聚合数据)
    private List<OrderDTO> orders;
    
    // 统计信息
    private Integer totalOrderCount;
    private BigDecimal totalAmount;
    
    // 原始数据类型
    private LocalDateTime userCreateTime;
}

使用场景:Service 层查询用户和订单数据,打包返回给 Controller

@Service
public class UserOrderService {
    public UserOrderDTO getUserWithOrders(Long userId) {
        UserDO user = userDao.findById(userId);
        List<OrderDO> orders = orderDao.findByUserId(userId);
        
        // 转换并聚合数据
        UserOrderDTO dto = new UserOrderDTO();
        dto.setUserId(user.getId());
        dto.setUsername(user.getUsername());
        dto.setEmail(user.getEmail()); // 直接暴露原始邮箱
        dto.setOrders(convertOrders(orders));
        dto.setTotalOrderCount(orders.size());
        return dto;
    }
}

VO(Controller 层返回前端)

@Data
public class UserOrderVO {
    // 前端展示字段(脱敏)
    private Long userId;
    private String username;
    private String maskedEmail; // 脱敏:a****@gmail.com
    
    // 订单数据(按需展示)
    private List<OrderVO> orders;
    
    // 格式化后的数据
    private String formattedTotalAmount; // "¥9,999.00"
    private String userCreateTimeDesc; // "2025-01-15 12:00"
    
    // 前端状态(计算得出)
    private String userStatusText; // "正常" 而非 1
    
    // 不包含:password, phone, raw status
}

使用场景:Controller 层转换 DTO 为 VO,适配前端展示

@RestController
@RequestMapping("/api/users")
public class UserOrderController {
    
    @Resource
    private UserOrderService userOrderService;
    
    @GetMapping("/{userId}/orders")
    public ResultVO<UserOrderVO> getUserOrders(@PathVariable Long userId) {
        UserOrderDTO dto = userOrderService.getUserWithOrders(userId);
        
        // DTO → VO 转换(关键一步!)
        UserOrderVO vo = new UserOrderVO();
        vo.setUserId(dto.getUserId());
        vo.setUsername(dto.getUsername());
        
        // 1. 脱敏处理
        vo.setMaskedEmail(maskEmail(dto.getEmail())); // a****@gmail.com
        
        // 2. 数据格式化
        vo.setFormattedTotalAmount(formatCurrency(dto.getTotalAmount()));
        vo.setUserCreateTimeDesc(formatDateTime(dto.getUserCreateTime()));
        
        // 3. 状态转换
        vo.setUserStatusText(convertStatusToText(user.getStatus())); // "正常"
        
        // 4. 按需过滤字段
        vo.setOrders(convertToOrderVO(dto.getOrders()));
        
        return ResultVO.success(vo);
    }
}

四、常见误区与反例

❌ 误区 1:DTO 和 VO 混用

// 错误:Controller 直接返回 DTO,暴露敏感信息
@RestController
public class UserController {
    public UserOrderDTO getUserOrders(Long userId) {
        return userOrderService.getUserWithOrders(userId); // ❌ 暴露了 password 等字段
    }
}

❌ 误区 2:VO 包含业务逻辑

// 错误:VO 包含计算逻辑
@Data
public class UserOrderVO {
    private BigDecimal orderAmount;
    private BigDecimal discount;
    
    // ❌ VO 只应承载数据,不应有业务方法
    public BigDecimal getPayAmount() {
        return orderAmount.subtract(discount); // 计算应在后端完成
    }
}

❌ 误区 3:过度使用 DTO 导致类爆炸

// 错误:为每个方法都创建 DTO
public class UserOrderService {
    public UserOrderDTO1 method1() { ... }
    public UserOrderDTO2 method2() { ... }
    public UserOrderDTO3 method3() { ... } // 维护噩梦
}

✅ 正确做法:合理复用 DTO,仅在字段差异大时创建新类

五、与其他对象的关系

在分层架构中,完整的对象体系如下:

完整对象体系

缩写全称作用位置示例
DOData Object持久层 ↔ 数据库UserDO(对应数据库表)
DTOData Transfer ObjectService ↔ ControllerUserOrderDTO(聚合数据)
VOView ObjectController ↔ 前端UserOrderVO(脱敏+格式化)
BOBusiness ObjectService 层内部OrderBO(处理业务逻辑)
POPersistant Object同 DO(老叫法)UserPO
AOApplication Object应用层参数LoginAO(登录参数)

六、最佳实践总结

1. 转换时机明确

// 分层转换
DAO层 → Service层 → Controller层 → 前端
  DO   →   DTO    →    VO      → JSON

2. 命名规范

// DTO 命名:业务 + DTO
UserDTO, OrderDTO, UserOrderDTO
 
// VO 命名:页面/组件 + VO
OrderListVO, OrderDetailVO, UserProfileVO

3. 工具类辅助转换

@Component
public class UserConverter {
    
    // DO → DTO
    public UserDTO toDTO(UserDO userDO) {
        UserDTO dto = new UserDTO();
        BeanUtils.copyProperties(userDO, dto);
        return dto;
    }
    
    // DTO → VO(关键:脱敏+格式化)
    public UserVO toVO(UserDTO userDTO) {
        UserVO vo = new UserVO();
        vo.setUserId(userDTO.getUserId());
        vo.setUsername(userDTO.getUsername());
        vo.setMaskedEmail(this.maskEmail(userDTO.getEmail()));
        vo.setStatusText(this.convertStatus(userDTO.getStatus()));
        return vo;
    }
    
    private String maskEmail(String email) {
        // 脱敏逻辑
        return email.replaceAll("(?<=.).(?=.*@)", "*");
    }
}

4. 统一返回结构

@Data
public class ResultVO<T> {
    private int code;      // 状态码
    private String message; // 提示信息
    private T data;        // 实际数据(VO/DTO)
    
    public static <T> ResultVO<T> success(T data) {
        ResultVO<T> result = new ResultVO<>();
        result.setCode(200);
        result.setMessage("success");
        result.setData(data);
        return result;
    }
}

七、总结

规则DTOVO
核心目标传输效率(减少调用次数)展示安全(适配前端+脱敏)
数据来源可能跨多个聚合通常基于单个 DTO
是否脱敏❌ 否✅ 必须脱敏
是否格式化❌ 原始数据✅ 前端友好格式
位置Service → ControllerController → 前端

设计原则先设计 VO 满足前端需求,再反推 DTO 需要聚合哪些数据,最后确定 DO 的数据库设计。

一句话总结DTO 为服务层效率而生,VO 为前端安全而生

到此这篇关于Java中DTO和VO区别的文章就介绍到这了,更多相关Java DTO和VO区别内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文