java:POJO、BO、VO、PO、DO、DTO分别代表什么?怎么用?

核心概念总览

POJO是 Plain Old Java Object 的缩写,直译为"普通的、老式的 Java 对象"。

它指的是一个简单的、纯粹的 Java 对象,不依赖于任何特定的框架、接口或注解。

下面都属于POJO

缩写英文全称中文释义核心职责与使用场景
POPersistent Object持久化对象与数据库表结构一一对应,是数据层(DAO层)操作的核心。
DODomain Object领域对象领域驱动设计(DDD) 中,代表业务领域中的核心实体。
DTOData Transfer Object数据传输对象用于进程/服务间的数据传输,通常用于远程调用等需要网络传输的场景。
VOView Object视图对象用于展示层,封装前端页面需要的数据。
BOBusiness Object业务对象在业务逻辑层,由多个PO或其它BO组合而成的复合业务对象。

详细解释与用法

1. PO (Persistent Object) - 持久化对象
  • 是什么:PO 是与数据库表结构直接映射的 Java 对象。每个属性通常对应数据库表的一个字段。

  • 怎么用

    • 主要在 DAO / Mapper 层 使用。

    • 通过 MyBatis、Hibernate 等ORM框架进行增删改查操作。

    • 不应包含任何业务逻辑,只是一个纯粹的数据载体。

  • 示例
    假设有一张 user 表,有 id, username, password 字段。

    public class UserPO {
        private Long id;
        private String username;
        private String password;
        // getter, setter ...
    }
    
    // 在DAO/Mapper层使用
    @Mapper
    public interface UserMapper {
        UserPO selectById(Long id);
        void insert(UserPO userPO);
    }
2. DO (Domain Object) - 领域对象
  • 是什么:在领域驱动设计(DDD) 模式中,DO 是业务领域的核心实体,它不仅有数据,还包含与这个数据相关的业务行为(方法)。

  • 怎么用

    • 领域层 使用。

    • 它强调的是封装性行为,而不仅仅是数据的getter/setter。

    • 在很多项目中,如果没有严格采用DDD,PO 和 DO 常常被混用,即一个类既是PO也是DO。但在严谨的架构中,它们应该分离。

  • 示例
    一个银行领域的 Account 对象。

    public class AccountDO {
        private String accountNumber;
        private BigDecimal balance;
    
        // 行为:存款
        public void deposit(BigDecimal amount) {
            if (amount.compareTo(BigDecimal.ZERO) <= 0) {
                throw new IllegalArgumentException("存款金额必须大于0");
            }
            this.balance = this.balance.add(amount);
        }
    
        // 行为:取款(包含业务规则)
        public void withdraw(BigDecimal amount) {
            if (amount.compareTo(BigDecimal.ZERO) <= 0) {
                throw new IllegalArgumentException("取款金额必须大于0");
            }
            if (this.balance.compareTo(amount) < 0) {
                throw new RuntimeException("余额不足");
            }
            this.balance = this.balance.subtract(amount);
        }
        // getter ...
    }
3. DTO (Data Transfer Object) - 数据传输对象
  • 是什么:用于不同进程或服务之间传输数据的对象。在网络传输(如RPC、REST API)时,它作为数据的容器。

  • 怎么用

    • 服务层(Service)控制层(Controller) 之间,或者微服务之间传输。

    • 它的设计不依赖于数据库表结构,而是根据传输需求来定义字段。

    • 通常是一个纯数据结构,只有属性和getter/setter。

  • 示例
    在用户注册时,前端传来的数据可能不包括 id(由数据库生成),但包括密码确认字段。

    // 用于接收前端注册请求的DTO
    public class UserRegisterDTO {
        private String username;
        private String password;
        private String confirmPassword; // 这个字段在PO中不存在
        // getter, setter ...
    }
4. VO (View Object) - 视图对象
  • 是什么专门为前端界面展示而设计的对象。

  • 怎么用

    • Controller 层 组装并返回给前端。

    • 它的字段结构完全由前端页面的需求决定,通常会聚合多个PO/DTO的数据,或者对数据进行格式化。

    • 绝对不能包含敏感信息(如密码)。

  • 示例
    用户信息页面需要展示用户基本信息和他的订单数量。

    // 用于前端用户信息页面的VO
    public class UserProfileVO {
        private String username;
        private String nickname;
        private String avatarUrl;
        private Integer orderCount; // 这个数据可能来自Order服务
        // getter, setter ...
    }
    
    // 在Controller层使用
    @RestController
    public class UserController {
        @GetMapping("/user/profile")
        public UserProfileVO getUserProfile() {
            // ... 业务逻辑,组装VO
            return userProfileVO;
        }
    }
5. BO (Business Object) - 业务对象
  • 是什么:在业务逻辑层(Service层) 使用的对象,它代表一个复合的业务实体,通常由多个PO聚合而成。

  • 怎么用

    • Service 层 内部使用,用于封装复杂的业务逻辑。

    • 一个BO可能包含多个PO,或者包含对其他BO的引用。

  • 示例
    “订单”这个业务对象,不仅包含订单本身的信息(OrderPO),还包含订单下的商品列表(OrderItemPO列表)和用户信息(UserPO)。

    public class OrderBO {
        // 来自 order 表
        private OrderPO orderInfo;
        // 来自 order_item 表
        private List<OrderItemPO> orderItems;
        // 来自 user 表
        private UserPO user;
    
        // 可以在BO中计算业务属性,如总价
        public BigDecimal getTotalAmount() {
            return orderItems.stream()
                    .map(item -> item.getPrice().multiply(new BigDecimal(item.getQuantity())))
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
        }
        // getter, setter ...
    }

数据流转与转换

在一个典型的分层架构中,数据的流转如下:

数据库 <-> PO <-> DAO层 <-> Service层 <-> Controller层 <-> 前端

  • DAO层:操作 PO

  • Service层

    • 内部使用 BO 来封装业务。

    • 接收来自Controller的 DTO

    • 返回 BO 或直接是 PO 给Controller。

  • Controller层

    • 接收前端传来的 DTO(通常通过 @RequestBody)。

    • 调用Service层,获取 BO/PO

    • BO/PO 转换为 VO,返回给前端。

关键点:各层之间的对象不应该直接传递,而应该进行转换。例如,Service层返回的BO,在Controller层要转换成VO再返回。这避免了底层数据结构的变化直接影响到上层(如API接口)。

转换工具:为了避免繁琐的 setter/getter 代码,推荐使用 MapStructBeanUtils(谨慎使用,注意性能和数据覆盖问题)、ModelMapper 等工具进行对象转换。

总结与最佳实践

  1. 职责分离:每个对象都有自己的职责,不要用一个对象贯穿所有层。这符合软件设计的“单一职责原则”。

  2. 避免“大而全”的对象:不要创建一个包含所有字段的“User”类,然后希望它在所有地方都能用。这会导致数据冗余、安全隐患(如把密码返回给前端)和强耦合。

  3. 按需定义:VO、DTO 的结构应根据前端和接口的需求来灵活定义,而不是死板地对应数据库。

  4. 理解本质而非记名字:在不同的公司或团队中,对这些对象的称呼可能不同(例如,有人把所有对象都叫POJO)。最重要的是理解它们背后的设计意图:在不同的上下文中,使用最适合的数据模型

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值