繁体   English   中英

equals 和 hashCode 与 Spring 数据 JPA 和 Hibernate

[英]equals and hashCode with Spring Data JPA and Hibernate

在阅读了几篇文章、线程并进行了一些研究之后,现在我对在我的 Spring Boot 应用程序中实现适当的 equals 和 hashCode 方法感到完全困惑。

例如,我有以下 class:

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Entity
public class Recipe {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false)
    private String title;

    @Column(length = 100)
    private String description;

    @Column(nullable = false)
    private Integer prepTime;

    @Column(nullable = false)
    private Integer cookTime;

    @Column(nullable = false)
    private Integer servings;

    @Lob
    @org.hibernate.annotations.Type(type = "org.hibernate.type.TextType")
    @Column(nullable = false)
    private String instructions;

    @Column(nullable = false)
    @Enumerated(value = EnumType.STRING)
    private Difficulty difficulty;

    @Column(nullable = false)
    @Enumerated(value = EnumType.STRING)
    private HealthLabel healthLabel;

    @ManyToOne(optional = true, fetch = FetchType.LAZY)
    @JoinColumn(name = "category_id", referencedColumnName = "id")
    private Category category;

    @OneToMany(mappedBy = "recipe", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<RecipeIngredient> recipeIngredients = new ArrayList<>();
}

我遇到以下这些问题并尝试正确实现 equals 和 hashCode 方法时遇到麻烦:

1)据我所知,当有一个唯一字段可以用来区分一个人与另一个人时,例如 email 或用户名,那么只在 equals 和 hashCode 方法中使用这些字段就足够了。 真的吗?

2)如果除了 id 之外没有任何唯一字段,那么我应该将所有字段(id 和其他)添加到 equals 和 hashCode 方法实现中吗?

3)当使用 Hibernate 和数据 JPA 时,我是否应该采用与其他情况不同的方法,因为在 JPA 生命周期中有 4 个状态 transient、managed、removed 和 detached? 我认为在这种情况下不应使用 id 字段,因为它在瞬态模式下不存在? 正确的?

实现 equals() 和 hashCode() 方法时:

  1. 如果有可用于区分一个 object 与另一个的唯一字段,请仅在实现中使用该字段。

  2. 如果没有唯一字段,则在实现中使用包括 id 在内的所有字段。

  3. 当使用 Hibernate 和数据 JPA 时,不要在实现中使用 ID 字段,因为它不存在于瞬态 state 中,而是使用在所有状态中都存在的字段,例如唯一字段或所有字段。

equalshashCode的问题在于它们的合同对于任何可变实体都是无效的,而对于 JPA,实际上没有任何其他实体。 暂时忽略 JPA,根据定义,实体的 id 定义了它的身份。 所以它应该用于equalshashCode 但这要求 id 在实体中是不可修改的,但是 JPA 需要一个无参数构造函数和一种设置所有属性的方法,包括 id。

可能最好的解决方法是

  • 使用身份证。

  • 确保在设置 id 之前永远不会使用equalshashCode ,并且之后永远不会更改 id。 id 一旦设置后不更改通常不是问题,因为 id 不应该从一个值更改为另一个值。 问题是创建新实例。 同样,JPA 返回的实例不是问题,因为 JPA 将在将它们返回给您之前完全初始化它们。 在您的应用程序中创建新实例是问题所在。 在这里您有以下选项:

    1. 创建实例并立即分配一个 id。 UUID 非常适合这个。 它们可以在应用程序服务器上轻松高效地生成。 这可以在实体 class 上的 static 工厂方法中完成。缺点是 UUID 对人类来说很难使用,因为它们很长而且基本上是随机的。 它们也很大,比传统的序列号在数据库中吃得更多 memory。 但是具有如此多的行以至于这实际上是一个问题的用例很少见。

    2. 像大多数人一样在数据库中生成 id,并确保您的新实体在创建后立即得到保存。 这可以在存储库中的自定义方法中很好地完成。 但它确实要求您在一个地方设置所有必需的属性,这通常会成为一个问题。

使用一些其他应该是不可变的属性,例如帐户名称或 email 首先仅适用于极少数实体,即使对于那些现在不可变的事实并不意味着它会保持这种状态。

与其试图避免 JPA 造成的陷阱,不如您可以依赖它。 JPA 保证对于给定的 class 和 id 只有一个实例在持久性上下文中。 因此,只要您只在一个实体的单个会话/事务中工作并且不尝试比较分离的实体,就根本不需要实现equalshashCode

因为您已经在使用 lombok,所以您也可以使用@Data注释:

@Data 现在全部在一起:@ToString、@EqualsAndHashCode、所有字段上的@Getter、所有非最终字段上的@Setter 和@RequiredArgsConstructor 的快捷方式!

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM