Java 对象分层定义规范 :POJO的各种业务实现

75 阅读5分钟

前言

POJO(Plain Old Java Object)是 Java 编程中一个非常基础且重要的概念。简单来说,POJO 就是一个“普通、简单、纯粹”的 Java 对象。判断一个类是不是 POJO,通常遵循以下标准(严格定义,但是可以适当放宽):

  1. 不继承特定类
  2. 不实现特定接口
  3. 没有注解依赖

一个标准的 POJO 代码示例:

public class User {
    private String name;
    private Integer age;

    public User() {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}

本文主要要介绍POJO类在项目开发中常见的几种业务概念实现。

1. DO / PO

DO (Data Object) / PO (Persistent Object)

  • 中文名称:数据对象 / 持久化对象
  • 对应层次:持久层(数据库)
  • 定义:与数据库表结构一一对应的 Java 对象。
  • 特点
    • 属性名通常与数据库字段名一致(或通过下划线/驼峰转换)。
    • 只包含基础数据类型。
  • 场景:当执行 SQL 查询后,数据库返回的数据封装成的对象。
  • 示例
// 对应数据库表 user (id, name, password, age)
public class UserDO {
	private Long id;
	private String name;
	private String password; // 加密后的密码
	private Integer age;
    }

2. DTO

DTO (Data Transfer Object)

  • 中文名称:数据传输对象
  • 对应层次:在各层之间或网络之间传输
  • 定义:用于在不同层之间传输数据的对象。
  • 特点
    • 裁剪与聚合:它可以包含比 DO 更少或更多的字段。
    • 隐藏敏感信息:比如 DO 里有 password,传给前端的 DTO 就不应该有。
    • 组合数据:比如一个 DTO 可能包含 UserDO 和 OrderDO 的部分字段组合。
  • 场景
    • 接口返回(VO):传给前端的数据。
    • 远程调用:微服务之间调用时传输的数据。
    • 业务处理:一般业务方法内部处理时可以传递DTO。
  • 示例
// 用户查询接口返回给前端的数据
public class UserDTO {
	private Long id;
	private String name;
	private Integer age;
	// 注意:这里没有 password 字段,为了安全
}

3. BO

BO (Business Object)

  • 中文名称:业务对象
  • 对应层次:业务逻辑层
  • 定义:封装了业务逻辑处理的对象。
  • 特点
    • 它可能是多个 DO 的组合。
    • 它可能包含复杂的计算逻辑或业务规则。
    • 它是 Service 层内部流转的对象。
  • 场景
    • 比如“创建订单”业务,需要操作“用户DO”、“商品DO”、“优惠DO”,Service 层会把它们组装成一个 OrderBO 进行处理。
  • 示例
// 提交订单时的业务对象
public class OrderBO {
	private UserDO user;       // 购买用户信息
	private List<ProductDO> products; // 商品列表
	private BigDecimal totalPrice;    // 计算后的价格(业务逻辑算出来的)
	private String couponCode;        // 优惠券代码
}

4. VO

VO (View Object)

  • 中文名称:视图对象 / 展示对象
  • 对应层次:控制层 -> 前端(展示层)
  • 定义:专门用于展示给前端(网页、App)的数据对象。
  • 特点
    • 脱敏:通常会隐藏敏感字段(如手机号中间四位隐藏、不返回密码)。
    • 格式化:将后端的数据格式化为前端易读的形式(如:1.0 -> "VIP"Date -> "2023-10-01")。
    • 组装:可能聚合了多个 DO 或 BO 的数据,专门为了某个页面设计。
  • 场景
    • 列表页展示的数据摘要。
    • 详情页返回的完整信息。
    • 前端下拉框需要的选项数据。
  • 示例
// 用户详情页返回给前端的数据
public class UserDetailVO {
	
	private Long id;
	private String username;
	
	// 脱敏处理:手机号 138****1234
	private String phoneNumber; 
	
	// 格式化处理:将 Date 类型的生日转为字符串 "1990-01-01"
	private String birthdayStr;
	
	// 组装处理:数据库里存的是 0/1,前端展示 "男/女"
	private String genderDesc; 
	
	// 注意:这里绝对不包含 password 字段
}

5. CO

有两种用法:1. Config Object 2. Command Object

5.1 Config Object

  • 中文名称:配置对象 / 属性对象
  • 对应层次:基础设施层 / 配置层
  • 定义:专门用于接收和绑定配置文件(如 .yml.properties)中参数的 Java 对象。
  • 特点
    • 通常配合 @ConfigurationProperties 注解使用,指定配置文件的前缀。
    • 支持松散绑定(如 userName 可匹配 user-name 或 user_name)。
    • 同样支持 JSR-303 校验注解(如 @Min@Email),确保启动时配置合法。
  • 场景
    • 数据库连接参数(URL、Username、Password)。
    • 第三方接口的 AppKey 和超时时间。
    • 业务自定义的开关或阈值。
  • 示例
@Component
@ConfigurationProperties(prefix = "app.sms")
@Validated // 开启配置参数校验
public class SmsConfigCO {
    
    @NotBlank(message = "短信服务商Key不能为空")
    private String accessKey;

    @Min(value = 1, message = "重试次数最少为1次")
    private Integer retryTimes = 3; // 默认值

    private List<String> whiteList; // 支持列表类型绑定

    // 标准 Getter and Setter
}

5.2 Command Object

  • 中文名称:命令对象 / 请求对象
  • 对应层次:控制层
  • 定义:专门用于接收前端(或调用方)传入的参数的对象。
  • 特点
    • 通常对应 Controller 的方法参数。
    • 包含校验注解(如 @NotNull@Email)。
    • 有时也叫 VO (View Object) 或 ReqDTO
  • 场景
    • 前端提交的注册表单、查询条件等。
  • 示例
// 用户注册请求
public class UserRegisterCO {
	@NotBlank(message = "用户名不能为空")
	private String username;

	@NotBlank(message = "密码不能为空")
	private String password;
}

6. QO

Query (Query Object)

  • 中文名称:查询对象
  • 对应层次:控制层 -> 业务层 -> 持久层
  • 定义:专门用于封装查询条件、分页参数和排序规则的对象。
  • 特点
    • 参数数量不固定,通常包含多个可选条件(如时间范围、模糊搜索)。
    • 往往继承自一个基础的 BaseQuery(包含 pageNumpageSize 等分页属性)。
    • 字段通常是基本数据类型或简单对象,用于拼接 SQL 的 WHERE 子句。
  • 场景
    • 后台管理系统的列表筛选(按状态、按日期、按关键词搜索)。
    • App 中的商品筛选(按价格区间、按品牌)。
  • 示例
// 用户列表查询条件
public class UserQuery {
	private Integer pageNum = 1;      // 当前页
	private Integer pageSize = 10;    // 每页条数

	private String keyword;           // 关键词(搜姓名或手机号)
	private Integer status;           // 状态(0-禁用,1-启用)
	private LocalDate startDate;      // 注册开始时间
	private LocalDate endDate;        // 注册结束时间
	
	// Getter and Setter...
}