DO、BO、DTO、VO和POJO

  • DO(Data Object)是一个与数据库表结构对应的对象,也叫持久化对象。DO用来在DAO层和数据库进行交互,也可以在其他层使用,比如作为方法的参数或返回值。
  • BO(Business Object)是一个封装业务逻辑的对象。BO用来在Service层处理业务规则,比如校验、计算、转换等。BO可以包含一个或多个DO对象的信息,也可以不包含DO对象的信息。
  • DTO(Data Transfer Object)是一个用来传输数据的对象。DTO用来在不同层或不同系统之间传递数据,比如从Service层传递到Controller层或View层。DTO可以包含与视图相关的信息,也可以包含与业务相关的信息。
  • VO(View Object)是一个用来展示数据的对象。VO用来在View层表示视图模型,比如JSP、HTML等。VO可以包含与视图相关的信息,比如页面展示的数据、页面需要的样式等
  • POJO(Plain Ordinary Java Object)是一个普通的Java对象,它没有任何特殊的要求或约束,可以用来表示任何类型的数据。POJO通常是其他数据模型的基础,比如DO、DTO、BO、VO等。

在展示页/业务逻辑层/数据层 之间的关系图如下所示:

(来源:知乎 侵删)
img

┌─────────────────────────────────────────────────────────────┐
│                      前端 (Frontend)                          │
│                    Vue/React/小程序                           │
└───────────────────────────┬─────────────────────────────────┘
                            │ HTTP Request/Response
                            │ JSON 数据
┌───────────────────────────▼─────────────────────────────────┐
│                   Controller 层 (表现层)                      │
│  ┌─────────────────────────────────────────────────────┐    │
│  │  @RestController / @Controller                       │    │
│  │  ├── ReqVO/ReqDTO  ◄── 接收前端请求参数              │    │
│  │  └── RespVO/RespDTO ──► 返回给前端的数据             │    │
│  └─────────────────────────────────────────────────────┘    │
└───────────────────────────┬─────────────────────────────────┘
                            │ 调用 Service
                            │ 
┌───────────────────────────▼─────────────────────────────────┐
│                    Service 层 (业务层)                        │
│  ┌─────────────────────────────────────────────────────┐    │
│  │  @Service                                            │    │
│  │  ├── BO (Business Object) - 业务对象                │    │
│  │  ├── DTO - 服务间传输对象                           │    │
│  │  │                                                   │    │
│  │  │  业务逻辑处理:                                   │    │
│  │  │  • ReqVO → BO/DO 转换                           │    │
│  │  │  • 调用 Mapper 进行数据库操作                   │    │
│  │  │  • DO → VO 转换                                 │    │
│  └─────────────────────────────────────────────────────┘    │
└───────────────────────────┬─────────────────────────────────┘
                            │ 调用 Mapper/Repository
                            │
┌───────────────────────────▼─────────────────────────────────┐
│                 Mapper/DAO 层 (数据访问层)                   │
│  ┌─────────────────────────────────────────────────────┐    │
│  │  @Mapper / @Repository                               │    │
│  │  └── DO/PO/Entity - 数据库实体对象                  │    │
│  │                                                       │    │
│  │  操作: CRUD 方法                                     │    │
│  └─────────────────────────────────────────────────────┘    │
└───────────────────────────┬─────────────────────────────────┘
                            │ MyBatis/JPA/JDBC
                            │
┌───────────────────────────▼─────────────────────────────────┐
│                    数据库 (Database)                          │
│                  MySQL/PostgreSQL/Oracle                      │
└─────────────────────────────────────────────────────────────┘

ReqVO (Request View Object)

@Data
public class UserLoginReqVO {
    @NotBlank(message = "用户名不能为空")
    private String username;
    
    @NotBlank(message = "密码不能为空")
    private String password;
}
  • 作用: 接收前端请求参数
  • 特点: 包含校验注解 (@NotBlank, @Valid)
  • 命名: XxxReqVOXxxReqDTO

RespVO (Response View Object)

@Data
public class UserRespVO {
    private Long id;
    private String username;
    private String nickname;
    private String avatar;
    // 不包含敏感信息如密码
}
  • 作用: 返回给前端的数据
  • 特点: 只包含前端需要的字段,脱敏处理
  • 命名: XxxRespVOXxxVO

PageReqVO (分页请求)

@Data
public class UserPageReqVO extends PageParam {
    private String username;  // 查询条件
    private Integer status;   // 查询条件
}

DTO (Data Transfer Object)

@Data
public class UserDTO {
    private Long id;
    private String username;
    private String email;
    private List<RoleDTO> roles;  // 可以包含关联对象
}
  • 作用: 服务间传输数据(微服务架构)
  • 特点: 可以组合多个 DO 的数据
  • 使用场景: Service 调用 Service、RPC 调用

BO (Business Object)

@Data
public class OrderBO {
    private OrderDO order;          // 订单信息
    private List<OrderItemDO> items; // 订单明细
    private UserDO user;            // 用户信息
    
    // 业务方法
    public BigDecimal calculateTotal() {
        return items.stream()
            .map(OrderItemDO::getAmount)
            .reduce(BigDecimal.ZERO, BigDecimal::add);
    }
}
  • 作用: 封装复杂业务逻辑
  • 特点: 包含业务方法,可组合多个 DO
  • 使用场景: 复杂业务处理

DO (Data Object) / PO (Persistent Object)

@Data
@TableName("sys_user")
public class UserDO {
    @TableId(type = IdType.AUTO)
    private Long id;
    
    private String username;
    private String password;
    private String email;
    private Integer status;
    
    private LocalDateTime createTime;
    private LocalDateTime updateTime;
}
  • 作用: 与数据库表一一对应

  • 特点

    :

    • 字段与数据库列对应
    • 包含 ORM 注解 (@TableName, @TableId)
    • 不包含业务逻辑
  • 命名: XxxDOXxxPOXxxEntity

完整请求流程

// 1. Controller 层 - 接收请求
@RestController
@RequestMapping("/user")
public class UserController {
    
    @PostMapping("/create")
    public RespVO<Long> createUser(@Valid @RequestBody UserCreateReqVO reqVO) {
        // ReqVO → Service
        Long userId = userService.createUser(reqVO);
        return RespVO.success(userId);
    }
    
    @GetMapping("/{id}")
    public RespVO<UserRespVO> getUser(@PathVariable Long id) {
        // Service → RespVO
        UserRespVO user = userService.getUser(id);
        return RespVO.success(user);
    }
}

// 2. Service 层 - 业务处理
@Service
public class UserServiceImpl implements UserService {
    
    @Autowired
    private UserMapper userMapper;
    
    @Override
    public Long createUser(UserCreateReqVO reqVO) {
        // ReqVO → DO 转换
        UserDO userDO = BeanUtil.copyProperties(reqVO, UserDO.class);
        userDO.setPassword(passwordEncoder.encode(reqVO.getPassword()));
        
        // DO → Database
        userMapper.insert(userDO);
        
        return userDO.getId();
    }
    
    @Override
    public UserRespVO getUser(Long id) {
        // Database → DO
        UserDO userDO = userMapper.selectById(id);
        
        // DO → RespVO 转换
        return BeanUtil.copyProperties(userDO, UserRespVO.class);
    }
}

// 3. Mapper 层 - 数据访问
@Mapper
public interface UserMapper extends BaseMapper<UserDO> {
    // 继承 BaseMapper,自动拥有 CRUD 方法
}
实体类所在层作用特点示例
ReqVO/ReqDTOController接收前端请求包含校验注解UserLoginReqVO
RespVO/RespDTOController返回给前端只含必要字段UserRespVO
DTOService服务间传输可组合数据UserDTO
BOService业务处理含业务方法OrderBO
DO/PO/EntityMapper/DAO数据库映射对应表结构UserDO
Controller 层:
├── UserLoginReqVO      (登录请求)
├── UserCreateReqVO     (创建请求)
├── UserUpdateReqVO     (更新请求)
├── UserPageReqVO       (分页查询请求)
├── UserRespVO          (响应)
└── UserSimpleRespVO    (简化响应)

Service 层:
├── UserDTO             (传输对象)
└── UserBO              (业务对象)

Mapper 层:
└── UserDO              (数据库对象)

💡 为什么要分这么多对象?

1. 职责分离

  • 每层只关注自己的职责
  • Controller 不直接操作 DO

2. 安全性

// ❌ 直接返回 DO,可能泄露密码
return userDO;

// ✅ 转换为 VO,过滤敏感字段
return convertToVO(userDO);

3. 灵活性

// 数据库字段变更不影响接口
// UserDO 改名,只需修改转换逻辑

4. 可维护性

  • 各层独立,修改互不影响
  • 便于单元测试

🔧 转换工具推荐

    1. 默认使用 {@link cn.hutool.core.bean.BeanUtil} 作为实现类,虽然不同 bean 工具的性能有差别,但是对绝大多数同学的项目,不用在意这点性能
    1. 针对复杂的对象转换,可以搜参考 AuthConvert 实现,通过 mapstruct + default 配合实现

1. MapStruct

@Mapper
public interface UserConverter {
    UserConverter INSTANCE = Mappers.getMapper(UserConverter.class);
    
    UserRespVO toVO(UserDO userDO);
    UserDO toDO(UserCreateReqVO reqVO);
}

2. BeanUtils

UserRespVO vo = BeanUtil.copyProperties(userDO, UserRespVO.class);

📝 最佳实践总结

  1. Controller 只用 ReqVO/RespVO
  2. Service 用 DTO/BO 处理业务
  3. Mapper 只操作 DO
  4. 不同层的对象不要混用
  5. 使用工具类自动转换
  6. VO 中不要包含敏感信息

这样的分层设计让代码结构清晰、职责明确、易于维护!🎯

应用

假设我们有一个用户管理系统,其中包含以下实体类:

UserDO:表示数据库中用户表的数据对象。

UserBO:表示业务对象,用于在service层中封装用户相关的业务逻辑,可以包含多个DO对象。

UserDTO:表示数据传输对象,用于在service和controller层之间传输数据。

UserVO:表示视图对象,用于在controller层向前端页面展示数据。

User:表示POJO对象,用于在不同层之间转换数据。

首先定义一个DO类,用来映射数据库中的实体:

public class UserDO {
    private Integer id;
    private String username;
    private String password;
    private Integer age;
}

再定义一个BO类,用来封装业务逻辑:

public class UserBO {
    private UserDO userDO; // 包含一个UserDO对象
    private String role; // 包含一个业务逻辑相关的属性

    // 包含一个业务逻辑相关的方法,比如判断用户是否是管理员
    public boolean isAdmin() {
        return "admin".equals(this.role);
    }
}

接下来定义一个DTO类,用来传输数据:

public class UserDTO {
    private String username; // 包含一个与视图相关的属性,比如用户名
    private Integer age; // 包含一个与业务相关的属性,比如年龄
}

再定义一个VO类,用来展示数据:

public class UserVO {
    private String username; // 包含一个与视图相关的属性,比如用户名
    private String ageDesc; // 包含一个与视图相关的属性,比如年龄描述,比如“20岁以下
}

最后,定义一个POJO类:

public class User {
    private Integer id;
    private String username;
    private String password;
    private Integer age;
    private String role;
    //一些实体类转换方法,略
}

接下来演示如何在service、dao、controller层中使用.

DAO层

public interface UserDAO {
     // 根据用户名查询用户信息,返回UserDO对象
    UserDO findByUsername(String username);

    // 根据用户id更新用户信息,接收UserDO对象作为参数
    int updateById(UserDO userDO);
}

Service层

@Service
public class UserService {
    
    @Autowired
    private UserDao userDao;

    public boolean validateUser(String username, String password) {
        // 调用Dao层的方法,根据用户名查询用户信息,返回UserDO对象
        UserDO userDO = userDao.findByUsername(username);
        // 判断用户是否存在,以及密码是否正确
        if (userDO != null && userDO.getPassword().equals(password)) {
            return true;
        } else {
            return false;
        }
    }
    public UserDTO getUserInfo(String username) {
        // 调用Dao层的方法,根据用户名查询用户信息,返回UserDO对象
        UserDO userDO = userDao.findByUsername(username);
        // 判断用户是否存在
        if (userDO != null) {
            // 返回UserDTO对象
            return User.toUserDTO(userDO);
        } else {
            // 返回null
            return null;
        }
    }

    @Override
    public UserBO getUserInfo(Integer userId) {
         UserDO userDO = userDao.findByUserId(userId);
        // 判断用户是否存在
        if (userDO != null) {
            // 返回UserBO对象
            return User.toUserBO(userDO);
        } else {
            // 返回null
            return null;
        }
    }
}

Controller层:

@RestController
public class UserController {
    @Autowired
    private UserService userService;

    @RequestMapping("/info")
    public UserVO info(String username) {
        // 调用Service层的方法,根据用户名获取用户信息,返回UserDTO对象
        UserDTO userDTO = userService.getUserInfo(username);
        // 判断用户是否存在
        if (userDTO != null) {
            // 返回UserVO对象
            return User.toUserVO(userDTO);
        } else {
            // 返回null
            return null;
        }
    }
}
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐