一尘不染

重写持久实体的hashCode()和equals()方法的正确方法是什么?

hibernate

我有一个简单的班级角色:

@Entity
@Table (name = "ROLE")
public class Role implements Serializable {

    @Id
    @GeneratedValue
    private Integer id;
    @Column
    private String roleName;

    public Role () { }

    public Role (String roleName) {
        this.roleName = roleName;
    }

    public void setId (Integer id) {
        this.id = id;
    }

    public Integer getId () {
        return id;
    }

    public void setRoleName (String roleName) {
        this.roleName = roleName;
    }

    public String getRoleName () {
        return roleName;
    }
}

现在,我想重写其方法equals和hashCode。我的第一个建议是:

public boolean equals (Object obj) {
    if (obj instanceof Role) {
        return ((Role)obj).getRoleName ().equals (roleName);
    }
    return false;
}

public int hashCode () {
    return id; 
}

但是,当我创建新的Role对象时,其id为null。这就是为什么我对hashCode方法实现有一些问题。现在,我可以简单地返回,roleName.hashCode ()但是如果没有RoleName字段,该怎么办?我几乎可以肯定,组成更复杂的示例并不难,而通过返回其字段之一的hashCode不能解决该示例。

因此,我想查看一些指向相关讨论的链接,或者听听您解决此问题的经验。谢谢!


阅读 245

收藏
2020-06-20

共1个答案

一尘不染

鲍尔和金的书《 Java持久性与Hibernate》
建议不要将键字段用于equals和hashCode。他们建议您应该选择对象的业务密钥字段(如果没有人工密钥),然后使用它们来测试相等性。因此,在这种情况下,如果角色名称不是必填字段,则可以找到必要的字段并结合使用。在您发布的代码中,除了id之外,您还拥有角色名,角色名就是我想要的。

以下是第398页的引文:

我们认为,基本上每个实体类都应该具有 一些
业务密钥,即使它包含了该类的所有属性(这对于某些不可变的类也是适当的)。业务密钥是用户唯一标识特定记录的东西,而代理密钥是应用程序和数据库使用的东西。

业务密钥相等性
意味着equals()方法仅比较构成业务密钥的属性。这是避免先前提出的所有问题的理想解决方案。唯一的缺点是,首先需要额外的思想来识别正确的业务密钥。无论如何,都需要付出这种努力。如果您的数据库必须通过约束检查确保数据完整性,则标识任何唯一键非常重要。

我用来构造equals和hashcode方法的一种简单方法是创建一个toString方法,该方法返回“业务键”字段的值,然后在equals()和hashCode()方法中使用它。澄清:这是一种懒惰的方法,适用于我不关心性能的情况(例如,在内部为皱纹的内部webapp中),如果预计性能会成为问题,请自己编写方法或使用IDE的代码生成工具。

2020-06-20