Loading...

文章背景图

POJO 相关类型详解

2026-06-26
11
-
- 分钟
|

首先明确一个核心概念:POJO(Plain Old Java Object) 本身并不是某一种具体的"类型",而是一种设计理念——指普通的、不继承/实现特定框架类/接口的 Java 对象。在它之上,业界衍生出了各种带有特定职责的"对象类型"。以下是常见的几大类:


一、PO(Persistent Object)— 持久化对象

说明

也叫

DO (Data Object)

定义

与数据库表结构一一对应的 Java 对象

所在层

数据持久层(DAO / Repository 层)

核心特征

一个字段通常对应表中的一个列,是 ORM 框架(MyBatis、JPA/Hibernate)直接操作的对象

示例:

// 对应 user 表
public class UserPO {
    private Long id;
    private String username;
    private String password;
    private String email;
    private Integer status;
    private LocalDateTime createTime;
    private LocalDateTime updateTime;
}

使用场景/用途:

  • 作为 MyBatis Mapper / JPA Repository 的返回值和入参

  • 只在持久层内部使用,不应直接暴露给 Service 层以上的调用方

  • 当数据库字段变更时,只需改这一个类


二、DTO(Data Transfer Object)— 数据传输对象

说明

定义

用于跨层/跨进程传输数据的对象

所在层

各层之间(Controller → Service、Service → DAO、远程 RPC 调用等)

核心特征

只包含需要传输的字段,不包含业务逻辑

示例 1 — 接口返回 DTO:

// 只返回前端需要的字段,避免暴露敏感数据
public class UserDTO {
    private Long id;
    private String username;
    private String avatar;
    private String roleName;
}

示例 2 — 查询参数 DTO:

public class UserQueryDTO {
    private String keyword;
    private Integer pageNo;
    private Integer pageSize;
    private String sortBy;
}

使用场景/用途:

  • API 接口返回值:用 DTO 组装需要返回给前端的数据,隐藏敏感字段(如 password)

  • RPC/微服务调用:跨服务传参时用 DTO 定义契约,避免内部 PO 泄漏

  • 批量数据传输:减少网络开销,只传必需字段


三、VO(View Object / Value Object)— 视图对象 / 值对象

实际上 VO 有两种含义,需要区分:

3.1 VO = View Object(视图对象)

说明

定义

专门为前端展示定制的对象

所在层

Controller 层 → 前端

核心特征

通常由多个 PO/DTO 的字段组合而成,可能包含格式化后的数据

示例:

// 前端个人中心页需要的数据,来自多个表
public class UserProfileVO {
    private String username;
    private String nickname;
    private String avatarUrl;          // 拼接后的完整 URL
    private String levelName;          // 来自等级表
    private String registerTime;       // 已格式化的日期字符串
    private List<OrderBriefVO> recentOrders;
}

使用场景/用途:

  • 一个页面可能对应一个 VO,包含了该页面需要展示的所有数据

  • 可以对后端数据进行格式化、聚合、裁剪,让前端拿到的数据"开箱即用"

  • 避免前端多次请求或做复杂数据处理

3.2 VO = Value Object(值对象)—— DDD 概念

说明

定义

通过属性值来标识自身的对象,没有唯一标识(ID)

核心特征

不可变(Immutable),两个 VO 属性完全相同则视为相等

典型示例

Money、Address、Color、PhoneNumber

public class Address {
    private final String province;
    private final String city;
    private final String detail;
    
    // 只提供构造器 + getter,不提供 setter
    // 重写 equals() 和 hashCode()
    
    @Override
    public boolean equals(Object o) {
        // 所有字段都相同才相等
    }
}

使用场景/用途(DDD 中的 VO):

  • 表示描述性概念,如金额(金额+货币单位)、坐标、电话号码

  • 因为没有 ID,不可变、可共享、可替换


四、BO(Business Object)— 业务对象

说明

定义

封装业务逻辑的对象,包含完整的业务状态和行为

所在层

Service / 业务逻辑层

核心特征

可以包含多个 PO,同时包含对这些数据的操作方法

示例:

// 订单业务对象,包含订单头和明细
public class OrderBO {
    private OrderPO order;
    private List<OrderItemPO> items;
    private UserPO user;
    
    // 包含业务方法
    public BigDecimal calcTotalPrice() {
        return items.stream()
            .map(OrderItemPO::getSubTotal)
            .reduce(BigDecimal.ZERO, BigDecimal::add);
    }
    
    public boolean canCancel() {
        return order.getStatus() == OrderStatus.PENDING;
    }
}

使用场景/用途:

  • 当业务逻辑复杂,需要操作多个 PO 时,用 BO 将它们聚合

  • 把"散落在 Service 方法中的业务逻辑"迁移到 BO 的方法中,避免 Service 层膨胀

  • 也可以用在报表、统计等场景,封装计算逻辑


五、AO(Application Object)— 应用对象

说明

定义

应用层(Application Layer)协调多个领域对象完成用例的对象

所在层

应用服务层

核心特征

用例流程的抽象,不包含具体业务规则

使用场景/用途:

  • 在 DDD 架构中,AO 负责编排多个领域服务完成一个完整的用户用例

  • 较少直接使用,更多是一种架构概念


六、对比总览

类型

全称

所在层

核心用途

生命周期

PO/DO

Persistent/Data Object

持久层

表结构映射,ORM 操作

与数据库会话绑定

DTO

Data Transfer Object

全栈(跨层)

跨层/跨进程传数据

一次传输后销毁

VO (View)

View Object

Controller → 前端

前端数据展示

一次请求内

VO (Value)

Value Object

领域层

无 ID 的不可变值

随领域对象

BO

Business Object

业务逻辑层

封装业务状态+行为

一次业务操作

POJO

Plain Old Java Object

所有"干净"对象的统称


七、最佳实践建议

  1. 分层隔离原则:PO 不暴露给 Controller,DTO 不泄露给 DAO

  2. DO → DTO → VO 转换:使用 MapStruct、BeanUtils 等工具转换,不要手写大量 setter

  3. 不要滥用:简单的 CRUD 项目不必每种对象都建,分层过度会增加维护成本;复杂的业务系统(如电商、金融)则建议严格分层

  4. 命名规范:UserPO / UserDO / UserDTO / UserVO / UserBO,团队统一风格即可

原创

POJO 相关类型详解

本文链接: POJO 相关类型详解

本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。

文章目录