Java中DTO和VO的區(qū)別舉例詳解
前言
DTO(Data Transfer Object)和 VO(Value Object / View Object)是 Java 開發(fā)中用于不同層次間數(shù)據(jù)傳遞的對(duì)象,但它們的設(shè)計(jì)目的和使用場(chǎng)景有本質(zhì)區(qū)別。
一、概念定義
DTO(Data Transfer Object)
定義:用于在不同層或系統(tǒng)之間傳輸數(shù)據(jù)的對(duì)象
核心作用:減少遠(yuǎn)程調(diào)用次數(shù),打包多個(gè)數(shù)據(jù)一次性傳輸
典型場(chǎng)景:Service 層返回?cái)?shù)據(jù)給 Controller 層、微服務(wù)間調(diào)用
VO(Value Object / View Object)
定義:用于前端展示的數(shù)據(jù)對(duì)象,封裝頁面需要的數(shù)據(jù)
核心作用:適配前端展示需求,隱藏后端敏感字段
典型場(chǎng)景:Controller 層返回給前端
二、核心區(qū)別對(duì)比表
| 對(duì)比維度 | DTO (Data Transfer Object) | VO (View Object) |
|---|---|---|
| 設(shè)計(jì)目的 | 減少網(wǎng)絡(luò)調(diào)用,批量傳輸數(shù)據(jù) | 適配前端展示需求 |
| 數(shù)據(jù)范圍 | 可能包含多個(gè)聚合的數(shù)據(jù) | 針對(duì)單個(gè)頁面/組件定制 |
| 數(shù)據(jù)敏感度 | 可包含內(nèi)部字段 | 必須隱藏敏感字段(密碼、金額等) |
| 使用層次 | Service ↔ Controller、微服務(wù)間調(diào)用 | Controller ↔ 前端 |
| 字段格式化 | 通常是原始數(shù)據(jù)類型 | 包含格式化后的數(shù)據(jù)(如日期、金額顯示格式) |
| 變更頻率 | 相對(duì)穩(wěn)定 | 隨前端需求頻繁變化 |
三、詳細(xì)說明與代碼示例
場(chǎng)景:用戶訂單詳情查詢
數(shù)據(jù)庫實(shí)體(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-凍結(jié)
}DTO(Service 層返回)
@Data
public class UserOrderDTO {
// 用戶基本信息
private Long userId;
private String username;
private String email; // 可能包含敏感信息
// 訂單列表(跨聚合數(shù)據(jù))
private List<OrderDTO> orders;
// 統(tǒng)計(jì)信息
private Integer totalOrderCount;
private BigDecimal totalAmount;
// 原始數(shù)據(jù)類型
private LocalDateTime userCreateTime;
}使用場(chǎng)景:Service 層查詢用戶和訂單數(shù)據(jù),打包返回給 Controller
@Service
public class UserOrderService {
public UserOrderDTO getUserWithOrders(Long userId) {
UserDO user = userDao.findById(userId);
List<OrderDO> orders = orderDao.findByUserId(userId);
// 轉(zhuǎn)換并聚合數(shù)據(jù)
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
// 訂單數(shù)據(jù)(按需展示)
private List<OrderVO> orders;
// 格式化后的數(shù)據(jù)
private String formattedTotalAmount; // "¥9,999.00"
private String userCreateTimeDesc; // "2025-01-15 12:00"
// 前端狀態(tài)(計(jì)算得出)
private String userStatusText; // "正常" 而非 1
// 不包含:password, phone, raw status
}使用場(chǎng)景:Controller 層轉(zhuǎn)換 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 轉(zhuǎn)換(關(guān)鍵一步?。?
UserOrderVO vo = new UserOrderVO();
vo.setUserId(dto.getUserId());
vo.setUsername(dto.getUsername());
// 1. 脫敏處理
vo.setMaskedEmail(maskEmail(dto.getEmail())); // a****@gmail.com
// 2. 數(shù)據(jù)格式化
vo.setFormattedTotalAmount(formatCurrency(dto.getTotalAmount()));
vo.setUserCreateTimeDesc(formatDateTime(dto.getUserCreateTime()));
// 3. 狀態(tài)轉(zhuǎn)換
vo.setUserStatusText(convertStatusToText(user.getStatus())); // "正常"
// 4. 按需過濾字段
vo.setOrders(convertToOrderVO(dto.getOrders()));
return ResultVO.success(vo);
}
}四、常見誤區(qū)與反例
? 誤區(qū) 1:DTO 和 VO 混用
// 錯(cuò)誤:Controller 直接返回 DTO,暴露敏感信息
@RestController
public class UserController {
public UserOrderDTO getUserOrders(Long userId) {
return userOrderService.getUserWithOrders(userId); // ? 暴露了 password 等字段
}
}? 誤區(qū) 2:VO 包含業(yè)務(wù)邏輯
// 錯(cuò)誤:VO 包含計(jì)算邏輯
@Data
public class UserOrderVO {
private BigDecimal orderAmount;
private BigDecimal discount;
// ? VO 只應(yīng)承載數(shù)據(jù),不應(yīng)有業(yè)務(wù)方法
public BigDecimal getPayAmount() {
return orderAmount.subtract(discount); // 計(jì)算應(yīng)在后端完成
}
}? 誤區(qū) 3:過度使用 DTO 導(dǎo)致類爆炸
// 錯(cuò)誤:為每個(gè)方法都創(chuàng)建 DTO
public class UserOrderService {
public UserOrderDTO1 method1() { ... }
public UserOrderDTO2 method2() { ... }
public UserOrderDTO3 method3() { ... } // 維護(hù)噩夢(mèng)
}? 正確做法:合理復(fù)用 DTO,僅在字段差異大時(shí)創(chuàng)建新類
五、與其他對(duì)象的關(guān)系
在分層架構(gòu)中,完整的對(duì)象體系如下:

完整對(duì)象體系
| 縮寫 | 全稱 | 作用位置 | 示例 |
|---|---|---|---|
| DO | Data Object | 持久層 ↔ 數(shù)據(jù)庫 | UserDO(對(duì)應(yīng)數(shù)據(jù)庫表) |
| DTO | Data Transfer Object | Service ↔ Controller | UserOrderDTO(聚合數(shù)據(jù)) |
| VO | View Object | Controller ↔ 前端 | UserOrderVO(脫敏+格式化) |
| BO | Business Object | Service 層內(nèi)部 | OrderBO(處理業(yè)務(wù)邏輯) |
| PO | Persistant Object | 同 DO(老叫法) | UserPO |
| AO | Application Object | 應(yīng)用層參數(shù) | LoginAO(登錄參數(shù)) |
六、最佳實(shí)踐總結(jié)
1. 轉(zhuǎn)換時(shí)機(jī)明確
// 分層轉(zhuǎn)換 DAO層 → Service層 → Controller層 → 前端 DO → DTO → VO → JSON
2. 命名規(guī)范
// DTO 命名:業(yè)務(wù) + DTO UserDTO, OrderDTO, UserOrderDTO // VO 命名:頁面/組件 + VO OrderListVO, OrderDetailVO, UserProfileVO
3. 工具類輔助轉(zhuǎn)換
@Component
public class UserConverter {
// DO → DTO
public UserDTO toDTO(UserDO userDO) {
UserDTO dto = new UserDTO();
BeanUtils.copyProperties(userDO, dto);
return dto;
}
// DTO → VO(關(guān)鍵:脫敏+格式化)
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. 統(tǒng)一返回結(jié)構(gòu)
@Data
public class ResultVO<T> {
private int code; // 狀態(tài)碼
private String message; // 提示信息
private T data; // 實(shí)際數(shù)據(jù)(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;
}
}七、總結(jié)
| 規(guī)則 | DTO | VO |
|---|---|---|
| 核心目標(biāo) | 傳輸效率(減少調(diào)用次數(shù)) | 展示安全(適配前端+脫敏) |
| 數(shù)據(jù)來源 | 可能跨多個(gè)聚合 | 通常基于單個(gè) DTO |
| 是否脫敏 | ? 否 | ? 必須脫敏 |
| 是否格式化 | ? 原始數(shù)據(jù) | ? 前端友好格式 |
| 位置 | Service → Controller | Controller → 前端 |
設(shè)計(jì)原則:先設(shè)計(jì) VO 滿足前端需求,再反推 DTO 需要聚合哪些數(shù)據(jù),最后確定 DO 的數(shù)據(jù)庫設(shè)計(jì)。
一句話總結(jié):DTO 為服務(wù)層效率而生,VO 為前端安全而生。
到此這篇關(guān)于Java中DTO和VO區(qū)別的文章就介紹到這了,更多相關(guān)Java DTO和VO區(qū)別內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- 深扒Java中POJO、VO、DO、DTO、PO、BO、AO、DAO的概念和區(qū)別以及如何應(yīng)用
- java中PO、VO、BO、POJO、DAO、DTO、TO、QO、Bean、conn的理解
- java中VO和DTO之間的轉(zhuǎn)換實(shí)現(xiàn)
- java實(shí)現(xiàn)相同屬性名稱及相似類型的pojo、dto、vo等互轉(zhuǎn)操作
- Java后端中dto、vo、entity的區(qū)別淺析
- java中VO PO DTO POJO BO DO對(duì)象的應(yīng)用場(chǎng)景及使用方式
- 一篇文章讓你徹底搞懂Java中VO、DTO、BO、DO、PO
相關(guān)文章
MySQL如何設(shè)置自動(dòng)增長(zhǎng)序列SEQUENCE的方法
本文主要介紹了MySQL如何設(shè)置自動(dòng)增長(zhǎng)序列SEQUENCE的方法,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-12-12
淺談java調(diào)用Restful API接口的方式
這篇文章主要介紹了淺談java調(diào)用Restful API接口的方式,具有一定借鑒價(jià)值,需要的朋友可以參考下。2017-12-12
基于springboot微信公眾號(hào)開發(fā)(微信自動(dòng)回復(fù))
這篇文章主要介紹了基于springboot微信公眾號(hào)開發(fā)(微信自動(dòng)回復(fù)),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-11-11
Hibernate實(shí)現(xiàn)批量添加數(shù)據(jù)的方法
這篇文章主要介紹了Hibernate實(shí)現(xiàn)批量添加數(shù)據(jù)的方法,詳細(xì)分析了基于Hibernate執(zhí)行批量添加操作的具體步驟與相關(guān)實(shí)現(xiàn)代碼,需要的朋友可以參考下2016-03-03
Java動(dòng)態(tài)字節(jié)碼注入技術(shù)的實(shí)現(xiàn)
Java動(dòng)態(tài)字節(jié)碼注入技術(shù)是一種在運(yùn)行時(shí)修改Java字節(jié)碼的技術(shù),本文主要介紹了Java動(dòng)態(tài)字節(jié)碼注入技術(shù)的實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下2023-08-08

