【问题标题】:Problem with equals() method in HibernateHibernate中equals()方法的问题
【发布时间】:2011-11-06 02:30:24
【问题描述】:

我正在 Hibernate 中开发一个应用程序,其中有如下模型类:

public class Employee
{
    private int ID;
    private String name;
    private Department department;
    //other properties
    //constructors, getters and setters
}

请注意,ID 不是用户填充的值,而是使用 GenerationType.Identity 作为strategy 填充的。

另外我还有一个班级Department如下:

public class Department
{
    private int ID;
    private String name;

    private Set<Employee> employees; //this is actually a HashSet

    //other implementations
}

EmployeeDepartment 之间存在ManyToOne 双向关系。

所以要将新的Employee 添加到现有的Department,我执行以下操作

Department existingDepartment = ...;
Employee newEmployee = ...;

existingDepartment.addEmployee(newEmployee);
employee.setDepartent(existinDepartment);

session.save(newEmployee);

现在,如果两个 Employee 对象具有相同的 ID,则它们在概念上是相同的。所以我在Employee 类中的equals() 方法如下所示:

public boolean equals(Object o)
{
    if(!(o instanceOf Employee))
    {
        return false;
    }

    Employee other = (Employee)o;

    if(this.ID == o.etID())
    {
        return true;
    }

    return false;
}

现在的问题是当我创建一个new Employee(); 时,我没有它的ID,因为它会在持久化时被分配。所以当我说

existingDepartment.addEmployee(newEmployee);

Department 对象的内部HashSet 有效地使用了一个被破坏的equals() 方法[因为它使用成员变量来确定未正确初始化的相等性]。

这似乎是一个非常基本的问题,但是,我该如何解决呢?还是我设计的课程完全错误?还是应该重写我的equals 方法来比较其他值而不是ID,我猜这很荒谬。

【问题讨论】:

    标签: java hibernate


    【解决方案1】:

    在休眠中,通常你可以告诉它使用一个值,当它没有被保存到数据库时。例如,我使用-1 表示尚未存储的 ID。

    您应该像这样初始化您的 id 以确保您获得一致的行为。

    private int ID = -1;
    

    【讨论】:

    【解决方案2】:

    重写你的 equals 方法,使其在 o 为 null 时返回 false:

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final Employee other = (Employee) obj;
        if (this.id != other.id && (this.id == null || !this.id.equals(other.id))) {
            return false;
        }
        return true;
    }
    

    【讨论】:

    • 我的IDs 是ints,原语。 :-)
    • “我的 ID 是整数,原语。”这通常是一个非常糟糕的主意。
    • @Sean,除非必须,否则不应使用盒装原语。这可能是一个使用它们的好例子,但如果你一直使用所有装箱的整数,那么这是一种可怕的做法,你也会失去很多性能。
    • @Amir 总的来说,我同意(当然,尤其是当你在做算术时)。但对于持久实体,包装器是区分设置 ID 和未设置 ID 的最简单方法。
    • 同意解释为什么以及在何处使用包装器。
    【解决方案3】:

    这似乎是一个非常基本的问题,但是,我该如何解决呢?或者我是 设计我的课程完全错误?或者我的equals方法应该是 重写以比较其他值而不是 ID,我猜这会 太荒谬了。

    对此有两种不同的哲学。

    a) equals() / hashCode() 基于 DB id

    缺点:无法比较持久对象和非持久对象

    b) equals() / hashCode() 基于内容

    缺点:具有相同 id 的两个对象可能会不相等。

    我更喜欢第二种方法,从 Java 的角度来看,它更有意义(尽管从 DB 的角度来看,这是不可否认的)。 我要确保的唯一一件事是你永远不要混用这些方法。

    这个已经讨论过很多次了,顺便说一句:

    【讨论】:

    • +1 请注意,第二种方法是唯一不会破坏散列集合的方法,因为它会改变 equals() 和 hashCode() 所依赖的包含对象的属性.
    【解决方案4】:

    您可以添加具有不同非持久 id 的瞬态字段。 (也许你应该升级到“长” id)。 像这样的例子

    public class Employee {
      private static int lastTID = 0;
      private int ID = -1;
      private transient int tID;
     ..
     public Employee () {
        synchronized (getClass()) {
          tId = -- lastTID;
        }
     }
     public boolean equals(Object o) {
     ..
       Employee other = (Employee)o;
     ..
       if (ID != -1) {
        return ID == other.ID;
       } else {
        return other.ID == -1 && tID == other.tID;
       }
     }
    

    在任何情况下,您都必须确保没有已保存和未保存的 Employee 正在使用中。

    另一种策略是先保存员工,然后将其添加到部门

    【讨论】:

      猜你喜欢
      • 2012-03-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-02-17
      • 2021-11-24
      • 2011-04-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多