15

我有一个抽象 id 和 version 属性的 BaseEntity。这个类还实现了基于 PK (id) 属性的 hashcode 和 equals。

BaseEntity{

    Long id;
    Long version; 

public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + ((id == null) ? 0 : id.hashCode());
    return result;
}

public boolean equals(Object obj) {
    if (this == obj)
        return true;
    if (obj == null)
        return false;
    if (getClass() != obj.getClass())
        return false;
    BaseEntity other = (BaseEntity) obj;
    if (id == null) {
        if (other.id != null)
            return false;
    } else if (!id.equals(other.id))
        return false;
    return true;
}


}

现在两个实体 A 和 B 扩展 BaseEntity 如下

A extends BaseEntity{
    `B b`
     B getB(){return b;)
     void setB(B b){this.b=b;}
}

B extends BaseEntity{
}

object b1;
object a1;
a1.set(b1);
session.save(a1) //cascade save;

用惰性 b 关闭会话加载 a 并尝试 a1.getB().equals(b1) 给出错误但如果我与 a1.getB().getId().equals(b1.getId()) 进行比较,则给出真正的奇怪!我认为这是因为 java 辅助代理对象,无论如何要解决这个问题?

4

4 回答 4

37

为了能够延迟加载a.b关联,Hibernate 将b字段设置a为代理。代理是扩展 B 的类的实例,但不是 B。因此,将非代理 B 实例与代理 B 实例进行比较时,您的 equals() 方法将始终失败,因为它比较了两个对象的类:

if (getClass() != obj.getClass())
    return false;

对于 Hibernate 实体,您应该将其替换为

if (!(obj instanceof B)) {
    return false;
}

另外,请注意

  • Hibernate 建议不要实现equals()hashCode()使用 ID,而是使用自然标识符。使用 ID 实现它可能会导致问题,因为实体在保存并生成 ID 之前没有 ID
  • 使用实体继承时,问题更严重。假设 B 是两个子实体 B1 和 B2 的超类。a.bHiberante在加载之前无法知道是哪种类型(B1 或 B2) 。所以a.b将被初始化为一个代理,它是 B 的子类,但不是 B1 或 B2 的子类。所以hashCode()andequals()方法应该在 B 中实现,而不是在 B1 和 B2 中被覆盖。如果两个 B 实例是 B 的实例并且具有相同的标识符,则它们应被视为相等。
于 2012-07-02T19:13:46.660 回答
13

我用Hibernate.getClass 了很多年,我从来没有注意到一个问题:

@Override    
public boolean equals(final Object obj) {
    if (this == obj) {
        return true;
    }
    if (obj == null) {
        return false;
    }
    if (Hibernate.getClass(this) != Hibernate.getClass(obj)) {
        return false;
    }

    ... check for values

    return true;
}
于 2014-04-24T12:07:06.410 回答
3

你也可以让它以这种方式工作,如果你不知道哪个实例是 B 很有用(如果你equals在超类中可能会发生)

if (HibernateProxyHelper.getClassWithoutInitializingProxy(this) != HibernateProxyHelper.getClassWithoutInitializingProxy(obj)) 
    return false
于 2014-04-10T09:50:06.260 回答
-1

这主要是标准 Java 继承的影响。

a1.getB().equals(b1)使用Object.equals()(除非你在你的类中重写了 equals() ),只有当 a1.getB() 和 b1 是同一个实例时才返回 true。我不知道你到底做了什么(你的代码格式被破坏了),但看起来你已经a在不同的会话中再次加载,所以你得到了一个新的aand实例a.getB(),因此Object.equals()返回 false。

a1.getB().getId().equals(b1.getId())uses Long.equals(),如果 long 值相同(即使对于 Long 对象的不同实例)返回 true,并且这些值显然是相同的。

于 2012-06-13T11:45:10.383 回答