Hibernate等于和代理

Jig*_*ekh 11 proxy hibernate equals

我有一个BaseEntity抽象id和版本属性.此类还基于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;
}


}
Run Code Online (Sandbox Code Playgroud)

现在,两个实体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;
Run Code Online (Sandbox Code Playgroud)

close会话加载一个懒惰的b并尝试a1.getB().equals(b1)给出false,但如果我与a1.getB()进行比较.getId().equals(b1.getId())然后给出真正的奇怪!! 我认为这是因为java辅助代理对象,反正要解决这个问题?

JB *_*zet 21

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

if (getClass() != obj.getClass())
    return false;
Run Code Online (Sandbox Code Playgroud)

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

if (!(obj instanceof B)) {
    return false;
}
Run Code Online (Sandbox Code Playgroud)

另外,请注意

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


Ral*_*lph 12

我使用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;
}
Run Code Online (Sandbox Code Playgroud)

  • 除了您的域类现在几乎不与Hibernate耦合.假设您想要与其他不使用hibernate的应用程序共享您的域名..现在您遇到了麻烦.不要误解我会起作用,但它闻起来非常糟糕.此外,如果hibernate人决定移动\删除代理工具,你升级hibernate - 域将不再编译. (5认同)