Skip to content

1. Entity(实体对象)

定义:与数据库表结构一一映射的 Java 类,通常使用 JPA/Hibernate 注解(如 @Entity@Table)。 • 核心职责: • 表示数据库表的字段和关系(如 @OneToMany)。 • 通过 DAO/Repository 层进行持久化操作(增删改查)。 • 特点: • 字段与数据库列直接对应。 • 通常不包含业务逻辑(贫血模型)。 • 可能包含 JPA 懒加载、级联操作等特性。 • 示例

java
@Entity
@Table(name = "user")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String username;
    private String password;
    // 省略 getter/setter
}

2. VO(View Object,视图对象)

定义:专为前端展示设计的对象,用于封装需要呈现的数据。 • 核心职责: • 适配前端需求(如字段组合、格式化)。 • 隐藏敏感字段(如密码、内部状态)。 • 特点: • 字段可能与多个 Entity 或 DTO 关联。 • 无业务逻辑,仅关注数据展示。 • 通常由 Controller 层返回给前端。 • 示例

java
public class UserVO {
    private String username;
    private String email;
    private String lastLoginTime; // 格式化后的日期
    private Integer orderCount;  // 聚合其他表的数据
    // 省略 getter/setter
}

3. BO(Business Object,业务对象)

定义:封装复杂业务逻辑和状态的对象,通常与领域驱动设计(DDD)结合。 • 核心职责: • 实现核心业务规则(如订单价格计算、状态流转)。 • 协调多个 Entity 或外部服务。 • 特点: • 充血模型:包含数据字段和业务方法。 • 不直接映射数据库,可能聚合多个 Entity。 • 由 Service 层创建和操作。 • 示例

java
public class OrderBO {
    private User user;
    private List<Product> products;
    private Coupon coupon;

    // 业务方法:计算订单总价
    public BigDecimal calculateTotalPrice() {
        BigDecimal total = products.stream()
            .map(Product::getPrice)
            .reduce(BigDecimal.ZERO, BigDecimal::add);
        if (coupon != null) {
            total = coupon.applyDiscount(total);
        }
        return total;
    }
}

4. DAO(Data Access Object,数据访问对象)

定义:负责与数据库交互的组件,封装 CRUD 操作。 • 核心职责: • 提供对 Entity 的增删改查方法。 • 处理复杂查询(如分页、关联查询)。 • 特点: • 不包含业务逻辑,仅关注数据操作。 • 在 Spring 中通常由 JpaRepository 或 MyBatis Mapper 实现。 • 示例

java
public interface UserDao extends JpaRepository<User, Long> {
    // 自动生成查询方法
    User findByUsername(String username);

    // 自定义 JPQL
    @Query("SELECT u FROM User u WHERE u.email LIKE %:domain")
    List<User> findByEmailDomain(@Param("domain") String domain);
}

对比表格

组件用途数据来源是否含业务逻辑分层位置
Entity映射数据库表结构数据库表字段否(贫血模型)DAO/Repository 层
VO前端展示数据适配多个 Entity/DTO 的聚合Controller 层
BO封装复杂业务逻辑多个 Entity/服务调用结果是(充血模型)Service 层
DAO数据库操作(CRUD)数据库DAO/Repository 层

典型交互流程

text
[前端请求]
→ Controller 接收请求,使用 **VO** 接收参数或返回结果
→ Service 层调用 **DAO** 操作 **Entity**,或创建 **BO** 处理业务逻辑
→ **DAO** 读写数据库,返回 **Entity**
→ Service 将 **Entity** 转换为 **VO** 或 **DTO**
→ Controller 返回 **VO** 给前端

最佳实践

1. Entity 与 VO 的隔离

不要直接返回 Entity 给前端:避免暴露敏感字段(如 password)。 • 使用 MapStruct 或手动转换

java
@Mapper
public interface UserConverter {
    UserConverter INSTANCE = Mappers.getMapper(UserConverter.class);

    @Mapping(target = "lastLoginTime", source = "lastLogin", dateFormat = "yyyy-MM-dd")
    UserVO toVO(User user);
}

2. BO 的设计原则

高内聚:将相关业务逻辑集中到 BO 中。 • 避免依赖 DAO:BO 应通过 Service 层获取数据,而非直接调用 DAO。

java
// 正确:由 Service 层提供数据
public class OrderService {
    private OrderDao orderDao;

    public void processOrder() {
        List<Order> orders = orderDao.findAll();
        OrderBO orderBO = new OrderBO(orders);
        orderBO.validateStock();
    }
}

3. DAO 的职责边界

仅关注数据操作:不处理业务规则(如价格计算)。 • 复杂查询封装:使用 @Query 或 QueryDSL,避免在 Service 层拼接 SQL。


常见问题

Q1:Entity 可以直接作为 DTO 使用吗?

不推荐:Entity 可能包含敏感字段或 JPA 懒加载代理,直接暴露会导致序列化问题(如 LazyInitializationException)。

Q2:BO 和 Service 的区别是什么?

BO:封装原子业务逻辑(如订单价格计算)。 • Service:协调多个 BO 或 DAO,处理事务和流程调度。

Q3:VO 和 DTO 有什么区别?

DTO:用于层间数据传输(如 Service → Controller),字段接近 Entity。 • VO:专为前端定制,字段可能高度重组或格式化。


总结

Entity 是数据库的映射,DAO 是数据库的操作者。 • VO 是前端的适配器,BO 是业务逻辑的容器。 • 通过清晰的分层和职责分离,可以提升代码的可维护性和扩展性。

✨ 网站运行时间: 3年11月15天 ❤️ 道阻且长,行则将至 - 微信号: heikedreamer