返回

打造完美的 JPA 实体:最佳实践解析

java

完美 JPA 实体的艺术

简介

作为一名久经考验的 JPA 开发人员,我在创建实体方面遇到了各种挑战,比如访问类型、不可变属性、equals/hashCode 等。为了应对这些挑战,我精心整理了一系列最佳实践,旨在帮助你构建健壮且高效的 JPA 实体。

实体设计

### 实体类

  • 实现 Serializable: JPA 规范要求实体实现 Serializable,这有助于实体的序列化和反序列化。
  • 构造函数: 实体应该有一个带有所有必需字段的构造函数,并提供一个包私有的默认构造函数,以便 Hibernate 等 JPA 提供者进行代理生成和高效数据检索。

### 字段/属性

  • 字段访问: 通常,字段访问比属性访问更清晰、封装性更好。
  • 不可变字段: 省略不可变字段的设置器,它们只能通过构造函数设置。
  • 属性访问: 可以将属性设置为私有,但为了提高 Hibernate 性能,建议将其设置为包私有或公共。

### equals/hashCode

  • 不要使用生成 ID: 在持久化之前,ID 通常为空,因此不应将其用于 equals/hashCode 比较。
  • 唯一业务密钥: 优先使用不可变的唯一业务密钥进行 equals/hashCode 比较。
  • UUID: 如果没有可用的唯一业务密钥,可以使用在实体初始化时创建的非瞬态 UUID。
  • 相关实体: 切勿在 equals/hashCode 比较中引用相关实体,只需比较它们的 ID。

示例实体

@Entity
@Table(name = "ROOM")
public class Room implements Serializable {

    @Id
    @GeneratedValue
    private Integer id;

    @Column(name = "number")
    private String number; // immutable

    @Column(name = "capacity")
    private Integer capacity;

    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    @JoinColumn(name = "building_id")
    private Building building; // immutable

    // Constructor
    // ...

    // Equals/hashCode
    // ...
}

结论

遵循这些最佳实践将有助于你创建健壮且高效的 JPA 实体,这些实体能够有效地存储和管理数据。记住,JPA 实体设计是一个需要仔细考虑和权衡的迭代过程。

常见问题解答

  1. 为什么不推荐使用属性访问?

    属性访问在需要附加逻辑或自定义行为时很有用,但这会使实体类更加复杂。

  2. JPA 提供者是否强制要求实现 Serializable?

    并非所有 JPA 提供者都强制要求实现 Serializable,但大多数都建议实现它以确保序列化和反序列化。

  3. 使用 UUID 的好处是什么?

    UUID 是一种非瞬态的唯一标识符,即使在实体持久化之前也能使用。

  4. 为什么在 equals/hashCode 比较中不要使用相关实体?

    如果相关实体是代理,则在 equals/hashCode 比较中引用它们会导致代理初始化,从而降低性能。

  5. equals/hashCode 的最佳实践是否适用于所有 JPA 提供者?

    这些最佳实践适用于大多数 JPA 提供者,但具体实施可能有所不同。请参阅特定 JPA 提供者的文档以了解具体的指南。