后端开发 DTO-Entity-VO 转换模式详解

目录

[🧩 一、数据流走向(总览)](#🧩 一、数据流走向(总览))

[⚙️ 二、DTO → Entity:在业务层中完成](#⚙️ 二、DTO → Entity:在业务层中完成)

[⚙️ 三、Entity → VO:返回时的转换](#⚙️ 三、Entity → VO:返回时的转换)

[🧠 四、关键点总结](#🧠 四、关键点总结)

[✨ 五、推荐实践(让代码更干净)](#✨ 五、推荐实践(让代码更干净))

[1. 使用工具类简化拷贝](#1. 使用工具类简化拷贝)

[2. 把转换逻辑封装成"转换器类"](#2. 把转换逻辑封装成“转换器类”)

[✅ 六、一句话总结](#✅ 六、一句话总结)


在现代 Spring Boot Web 开发中,为了实现前后端分离、保证数据安全和明确分层,我们通常会使用三种类型的 Java 对象来处理数据:DTO、Entity 和 VO。

这个模式的核心思想是:在不同的应用层之间,使用专门的对象进行数据传输。

🧩 一、数据流走向(总览)

下面是-个标准 Web 请求的数据流转过程:

复制代码
前端 JSON 请求体
      ↓
DTO(数据传输对象)     ← 【控制器层 Controller】(用于接收输入)
      ↓
Entity(实体对象)      ← 【业务层 Service】(转换为 Entity,用于保存到数据库)
      ↓
数据库
      ↓
Entity(实体对象)      ← 【业务层 Service】(从数据库取出)
      ↓
VO(视图对象)        ← 【控制器层 Controller/Service】(转换为 VO,用于封装返回前端)
      ↓
前端 JSON 响应体

⚙️ 二、DTO → Entity:在业务层中完成

DTO (Data Transfer Object) :数据传输对象。它的唯一职责是接收前端传来的数据

当 Controller 接收到前端的 JSON 请求时,Spring MVC 会自动将其封装为 DTO 对象。

复制代码
// Controller
@PostMapping("/register")
public ApiResponse<Long> register(@Valid @RequestBody UserRegisterRequest dto) {
    // 此时,dto 对象就是前端传来的 JSON 数据
    // 然后我们把 dto 传给业务层
    Long userId = userService.register(dto);
    return ApiResponse.success("注册成功", userId);
}

Entity (实体) :数据库实体对象。它严格对应数据库中的表结构

这一步转换发生在 Service (业务) 层。Service 层负责核心业务逻辑,它接收 DTO,然后将其转换为 Entity,并补充业务所需的其他字段(比如默认权限、初始积分、加密密码等)。

复制代码
// ServiceImpl
public Long register(UserRegisterRequest dto) {
    // 1. 手动将 DTO 字段拷贝给 Entity
    User user = new User();
    user.setUsername(dto.getUsername());
    // 业务逻辑:密码需要加密
    user.setPassword(passwordEncoder.encode(dto.getPassword()));
    // 业务逻辑:昵称默认为用户名
    user.setNickname(StringUtils.hasText(dto.getNickname()) ? dto.getNickname() : dto.getUsername());
    user.setPhone(dto.getPhone());
    user.setEmail(dto.getEmail());
    
    // 2. 也可以用工具类来简化拷贝
    // BeanUtils.copyProperties(dto, user);
    // (注意:使用工具类后,仍需手动处理密码加密等特殊逻辑)
    
    // 3. 补充前端不传的业务字段
    user.setRole("user");
    user.setStatus(1);
    user.setCreditScore(100);

    // 4. 通过 MyBatis-Plus 将 Entity 存入数据库
    save(user);
    
    return user.getId();
}

这一步的作用

  1. 把前端的请求参数(DTO)转换成数据库对应的实体对象(Entity)。

  2. 在转换过程中执行业务逻辑(如加密、设置默认值)。

⚙️ 三、Entity → VO:返回时的转换

VO (View Object) :视图对象。它专门用于封装后端需要返回给前端的数据

我们通常不会 直接把 Entity 返回给前端,因为 Entity 里可能包含敏感字段(比如 passwordsalt)或者前端不需要的字段(比如 is_deletedupdate_time)。

VO 的作用就是只挑选前端需要的字段。

示例:

复制代码
// 专门用于前端展示的 UserVO
public class UserVO {
    private Long id;
    private String username;
    private String nickname;
    private String avatar;
    private String email;
}

转换可以在 Service 层完成,也可以在 Controller 层完成(推荐在 Service 层)。

复制代码
// ServiceImpl 中...
// 假设我们从数据库查到了 user (Entity)
User user = getById(userId);

// 1. 转换为 VO
UserVO vo = new UserVO();
vo.setId(user.getId());
vo.setUsername(user.getUsername());
vo.setNickname(user.getNickname());
vo.setAvatar(user.getAvatar());
vo.setEmail(user.getEmail());

// 2. 或者同样用工具类
// BeanUtils.copyProperties(user, vo);

// 3. 在 Controller 返回 VO
return ApiResponse.success("查询成功", vo);

前端最终拿到的 JSON 就会非常干净:

复制代码
{
  "success": true,
  "message": "查询成功",
  "data": {
    "id": 1001,
    "username": "tom",
    "nickname": "Tom",
    "avatar": "avatar/default.png",
    "email": "tom@demo.com"
  }
}

🧠 四、关键点总结

转换方向 发生位置 意义
DTO → Entity Service 把前端请求数据转换为数据库对象(用于保存
Entity → VO ServiceController 把数据库对象转换为前端可展示的数据(用于返回

✨ 五、推荐实践(让代码更干净)

1. 使用工具类简化拷贝

手动 setget 非常繁琐且容易出错。

  • Spring 自带 : BeanUtils.copyProperties(source, target);

  • 常用增强库:

    • MapStruct(推荐):在编译时自动生成类型安全的转换代码,性能极高。

    • ModelMapper:在运行时通过反射自动映射,非常灵活但性能略慢。

2. 把转换逻辑封装成"转换器类"

当转换逻辑变多时,可以创建一个专门的 Convert 类(或接口),让 Service 层保持干净。

复制代码
public class UserConvert {

    // 使用 MapStruct 的示例
    // @Mapper(componentModel = "spring")
    // public interface UserConvert {
    //     UserConvert INSTANCE = Mappers.getMapper(UserConvert.class);
    //     User toEntity(UserRegisterRequest dto);
    //     UserVO toVO(User user);
    // }

    // 手动封装的示例
    public static User toEntity(UserRegisterRequest dto) {
        User user = new User();
        BeanUtils.copyProperties(dto, user);
        // ... 其他特殊处理
        return user;
    }

    public static UserVO toVO(User user) {
        UserVO vo = new UserVO();
        BeanUtils.copyProperties(user, vo);
        return vo;
    }
}

然后你的 Service 代码就可以简化为:

复制代码
// ServiceImpl
User user = UserConvert.toEntity(request);
// ... 处理加密等 ...
save(user);
return UserConvert.toVO(user);

✅ 六、一句话总结

环节 作用 代码位置
DTO 接收前端输入 Controller 入参
Entity 持久化到数据库 Service + Mapper
VO 返回前端展示 Controller 出参
DTO→Entity 在 Service 中转换 业务逻辑处理时
Entity→VO 在 Service 或 Controller 中转换 返回结果时
相关推荐
前端不太难2 小时前
没有文档模型,HarmonyOS PC 应用会发生什么?
华为·状态模式·harmonyos
GatiArt雷4 小时前
Libvio.link网站反爬机制深度剖析与合规爬虫策略研究
状态模式
Go_Zezhou5 小时前
render快速部署网站和常见问题解决
运维·服务器·开发语言·python·github·状态模式
共享家952713 小时前
搭建 AI 聊天机器人:”我的人生我做主“
前端·javascript·css·python·pycharm·html·状态模式
We1ky1 天前
从零到一:我的javascript记忆翻转卡牌游戏诞生记
状态模式
Elieal1 天前
Spring MVC 全局异常处理实战
spring·mvc·状态模式
Elieal1 天前
统一 JSON 格式,JacksonObjectMapper 定制 Spring Boot JSON 转换规则
spring boot·json·状态模式
前端不太难1 天前
HarmonyOS PC 应用,先做文档模型
华为·状态模式·harmonyos
前端不太难1 天前
HarmonyOS 走向 PC,应用模型正在重构
重构·状态模式·harmonyos
进击的小头2 天前
行为型模式:状态模式——嵌入式状态管理的优雅解决方案
c语言·状态模式