为什么sping-boot+ mabatis。写接口时要建好几个VO。各自用途时

在 Spring Boot 开发中,面对同一个业务(比如"用户管理"),新建好几个名字很像的类(VO、DTO、Entity)确实会让初学者感到头大。但这恰恰是后端工程化最核心的"基本功"。

为了让你更直观地理解,我们可以把后端开发想象成开一家餐厅:

数据库 (MySQL) = 仓库(存放最原始的食材)

Entity = 原始食材(带着泥土的土豆、没宰杀的鸡,和仓库里的库存一一对应)

DTO = 顾客点的菜单(顾客告诉厨师要什么、几分熟、不要葱)

VO = 端上桌的精美菜品(摆好盘、加了装饰,专门给顾客看和吃的)

BO = 后厨的加工半成品(厨师在厨房里把食材和配料组合起来,进行复杂烹饪的中间状态)

下面为你详细拆解它们各自的用途和为什么要分开:

Entity (实体类):和数据库打交道的"原始食材"

用途:它和数据库里的表是一一对应的。数据库表里有什么字段,Entity 里就有什么属性。

为什么不能直接给前端用?

安全问题:你的用户表里可能存了 password(密码)、salt(加密盐)、id_card(身份证号)等极度敏感的信息。如果直接把 Entity 返回给前端,这些隐私就全部泄露了。

冗余问题:表里可能有 create_time、update_time 等给后端运维看的审计字段,前端展示根本不需要。

DTO (Data Transfer Object):前端传给后端的"菜单"

用途:专门用来接收前端发来的请求参数(比如注册、登录、修改信息时提交的表单数据)。

核心价值:

参数校验:你可以在 DTO 的字段上加注解(比如 @NotNull 不能为空, @Email 必须是邮箱格式),在数据进入业务逻辑前就把不合法的请求拦截掉。

按需接收:前端登录只需要传 username 和 password,你就不需要让整个用户表的所有字段都参与进来。

VO (View Object):后端返回给前端的"精美菜品"

用途:专门用来包装返回给前端的数据。

核心价值:

数据脱敏:把 Entity 里的密码等敏感字段剔除掉。

数据格式化:比如数据库里的时间存的是 2026-04-29 10:06:33,前端可能需要展示成"刚刚"、"3小时前";或者把多个字段拼成一个(比如 firstName + lastName 拼成 fullName)。

数据聚合:比如前端要展示"用户详情页",不仅需要用户基本信息,还需要这个用户的"最近订单列表"。这时 VO 可以把 UserInfo 和 List 组合在一起返回,前端调一次接口就能拿到所有数据。

BO (Business Object):后厨的"加工半成品"(进阶)

用途:在 Service 业务层内部使用。当业务非常复杂,需要把好几个 Entity(比如订单表、用户表、商品表)的数据组合起来进行一系列计算时,就会用到 BO。它不直接对外(不暴露给前端或数据库),只服务于复杂的业务逻辑。

💡 为什么要这么麻烦?(核心价值)

你可能会问:"就一个小项目,我直接用 Entity 接收和返回不行吗?"

小项目确实可以,但一旦项目变大,不分层的后果就是灾难:

极高的安全风险:一不小心就把数据库里的密码、内部状态码直接暴露给了黑客。

牵一发而动全身(高耦合):假设你的数据库表加了一个字段,如果前后端都用 Entity,你就得去改前端代码、改接口文档。但如果用了 VO/DTO,数据库怎么改是后端内部的事,只要保证给前端的 VO 接口契约不变,前端完全无感知。

代码崩溃:前端传的参数和后端数据库的字段往往不是一一对应的,混用会导致大量的空指针异常或数据映射错误。

🛠️ 实际开发中的小技巧

从 Entity 到 VO,或者从 DTO 到 Entity,字段拷贝写大量的 set/get 方法确实很繁琐。在实际工作中,我们通常会使用一些工具来自动完成这些"搬运"工作,比如 MapStruct(性能最好,推荐)、ModelMapper 或者 Spring 自带的 BeanUtils。

总结一下它们的数据流向:

前端请求 (JSON) ➡️ DTO ➡️ Controller ➡️ Service (可能会转成 BO 或 Entity 处理) ➡️ MyBatis 操作数据库 (Entity) ➡️ 查出数据转成 VO ➡️ 返回给前端 (JSON)。

把这几个"O"分清楚,你的 Spring Boot 项目架构就会变得非常清晰、安全且易于维护!

相关推荐
Mahir085 小时前
Spring 循环依赖深度解密:从问题本质到三级缓存源码级解析
java·后端·spring·缓存·面试·循环依赖·三级缓存
RyFit6 小时前
SpringAI 常见问题及解决方案大全
java·ai
石山代码6 小时前
C++ 内存分区 堆区
java·开发语言·c++
绝知此事6 小时前
【算法突围 01】线性结构与哈希表:后端开发的收纳术
java·数据结构·算法·面试·jdk·散列表
无风听海6 小时前
C# 隐式转换深度解析
java·开发语言·c#
一只大袋鼠7 小时前
Git 进阶(二):分支管理、暂存栈、远程仓库与多人协作
java·开发语言·git
德思特8 小时前
从 Dify 配置页理解 RAG 的重要参数
java·人工智能·llm·dify·rag
YOU OU8 小时前
Spring IoC&DI
java·数据库·spring
один but you9 小时前
从可变参数到 emplace:现代 C++ 性能优化的核心组合
java·开发语言
是码龙不是码农9 小时前
ThreadPoolExecutor 7 个核心参数详解
java·线程池·threadpool