Master PO, VO, BO, DTO, DAO & POJO: When and How to Use Each in Java

This article explains the purpose, responsibilities, and typical pitfalls of six common Java objects—PO, DAO, BO, DTO, VO, and POJO—illustrates their flow in different architectural models, and recommends tools and best‑practice patterns for efficient conversion and clean layer separation.

macrozheng
macrozheng
macrozheng
Master PO, VO, BO, DTO, DAO & POJO: When and How to Use Each in Java

1. Responsibilities of the 6 Objects

Object design is about separation of concerns—each object does one thing and does it well.

1.1 PO (Persistent Object)

Maps 1:1 to a database table and only holds data structure.

Responsibility: Strict 1:1 mapping with the table, data storage only.

Features: Fields correspond exactly to table columns; no business logic (only getters/setters).

Code Example:

public class UserPO {
    private Long id; // table primary key
    private String name; // name column
}

1.2 DAO (Data Access Object)

Encapsulates all database CRUD operations, isolating business logic from storage details.

Responsibility: Provide CRUD methods, return PO or PO collections.

Features: Interface methods correspond to SQL operations.

Code Example:

public interface UserDao {
    // Query PO by ID
    UserPO findById(Long id);
    // Paginated query
    List<UserPO> findPage(@Param("offset") int offset, @Param("limit") int limit);
}

Underlying principle: DAO = Interface + Implementation + PO.

1.3 BO (Business Object)

Encapsulates core business logic and aggregates multiple PO objects to complete complex operations.

Responsibility: Core business logic, aggregation of PO.

Features: Contains state machines, validation rules, may hold multiple PO references.

Code Example: Order refund BO

public class OrderBO {
    private OrderPO orderPO;
    private List<OrderItemPO> items;
    public RefundResult refund(String reason) {
        if (!"PAID".equals(orderPO.getStatus())) {
            throw new IllegalStateException("Unpaid order cannot be refunded");
        }
        // calculate refund, call payment gateway, etc.
    }
}

1.4 DTO (Data Transfer Object)

Used for cross‑layer or cross‑service data transfer, shielding sensitive fields.

Responsibility: Transfer data, hide sensitive fields.

Features: Subset of PO fields (e.g., exclude password); implements Serializable.

Code Example:

public class UserDTO implements Serializable {
    private Long id;
    private String name;
}

1.5 VO (View Object)

Adapts data for front‑end display and may contain rendering logic.

Responsibility: Presentation layer, may format data (e.g., dates).

Features: Can aggregate fields from multiple tables.

Code Example:

public class OrderVO {
    private String orderNo;
    private String createTime; // formatted date
    private String userName; // from user table
    public String getStatusText() {
        return OrderStatus.of(this.status).getDesc();
    }
}

1.6 POJO (Plain Old Java Object)

Simple data container that can serve as PO, DTO, or VO.

Responsibility: Basic data holder.

Features: Only fields + getters/setters; no framework dependencies.

Typical Implementation: Lombok to generate boilerplate.

@Data
public class UserPOJO {
    private Long id;
    private String name;
}

2. Main Object Flow Models

Scenario 1 – Traditional three‑layer (DAO → DTO → VO)

Suitable for back‑office or utility applications.

Code Example: User query service

public UserDTO getUserById(Long id) {
    UserPO userPO = userDao.findById(id);
    UserDTO dto = new UserDTO();
    dto.setId(userPO.getId());
    dto.setName(userPO.getName()); // filter sensitive fields
    return dto;
}

public UserVO getUser(Long id) {
    UserDTO dto = userService.getUserById(id);
    UserVO vo = new UserVO();
    vo.setUserId(dto.getId());
    vo.setUserName(dto.getName());
    vo.setRegisterTime(formatDate(dto.getCreateTime()));
    return vo;
}

Pros: Simple and direct, good for CRUD.

Cons: Business logic may leak into the Service layer.

Scenario 2 – DDD (PO → DO → DTO → VO)

Fits complex domains such as e‑commerce or finance.

Key role: DO (Domain Object) replaces BO.

public class OrderDO {
    private OrderPO orderPO;
    private PaymentPO paymentPO;
    public void validatePayment() {
        if (paymentPO.getAmount() < orderPO.getTotalAmount()) {
            throw new PaymentException("Insufficient payment amount");
        }
    }
}

public OrderPaymentDTO pay(OrderPayCmd cmd) {
    OrderDO order = orderRepo.findById(cmd.getOrderId());
    order.validatePayment();
    return OrderConverter.toDTO(order);
}

Pros: High cohesion, suitable for complex rule systems.

Cons: More conversion layers increase development cost.

3. Efficient Conversion Tools

3.1 MapStruct – Compile‑time code generation

Principle: Annotation processor generates mapping code.

@Mapper
public interface UserConverter {
    UserConverter INSTANCE = Mappers.getMapper(UserConverter.class);
    @Mapping(source = "createTime", target = "registerDate")
    UserDTO poToDto(UserPO po);
}

public class UserConverterImpl {
    public UserDTO poToDto(UserPO po) {
        UserDTO dto = new UserDTO();
        dto.setRegisterDate(po.getCreateTime()); // auto‑assign
        return dto;
    }
}

Advantages: Zero reflection overhead, performance close to hand‑written code.

3.2 Dozer + Lombok – Annotation‑driven conversion

Combine Lombok for boilerplate and Dozer for XML/annotation field mapping.

@Data
public class UserVO {
    private String userId;
    private String userName;
}

<!-- Dozer mapping snippet -->
<field>
    <a>userId</a>
    <b>id</b>
</field>

Suitable scenario: Complex field‑name mismatches.

3.3 Manual Builder – Fine‑grained control

public class OrderVOBuilder {
    public OrderVO build(OrderDTO dto) {
        return OrderVO.builder()
            .orderNo(dto.getOrderNo())
            .amount(dto.getAmount() + "元")
            .statusText(convertStatus(dto.getStatus()))
            .build();
    }
}

4. Pitfall Guide

Pitfall 1 – Returning PO directly to the front‑end

public UserPO getUser(Long id) {
    // PO contains password!
    return userDao.findById(id);
}

Solution: Use DTO to filter fields or annotate sensitive fields with @JsonIgnore.

Pitfall 2 – Embedding business logic in DTO

public class OrderDTO {
    // Wrong! DTO should not contain business methods
    public void validate() {
        if (amount < 0) throw new Exception();
    }
}

Root cause: Confusing responsibilities of DTO and BO.

Pitfall 3 – Nested collection conversion causing N+1 queries

public class OrderVO {
    private List<ProductVO> products;
}

orderVO.setProducts(order.getProducts()
    .stream()
    .map(p -> convertToVO(p)) // triggers N+1 queries
    .collect(toList()));

Optimization: Batch queries + parallel conversion.

5. How to Choose an Object Model?

Summary

Single Responsibility: PO stores data, BO handles business, VO presents view – never cross boundaries.

Safety Isolation: PO never leaves DAO layer; VO never leaves Controller layer.

Performance First: Use MapStruct for large object conversion; batch queries for nested collections to avoid N+1.

Moderate Design: Systems with ≤10 tables can use POJO throughout; larger systems require strict layering.

There is no silver bullet in object design – understanding the business domain is more important than blindly applying patterns.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

BackendJavapojoObject Design
macrozheng
Written by

macrozheng

Dedicated to Java tech sharing and dissecting top open-source projects. Topics include Spring Boot, Spring Cloud, Docker, Kubernetes and more. Author’s GitHub project “mall” has 50K+ stars.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.