【问题标题】:Hibernate equals and proxyHibernate 等于和代理
【发布时间】:2012-06-13 10:39:59
【问题描述】:

我有一个抽象 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) 给出 false 但是如果我与 a1.getB().getId().equals(b1.getId()) 进行比较,那么真的很奇怪! 我认为这是因为 java 辅助代理对象,无论如何要解决这个问题?

【问题讨论】:

    标签: hibernate proxy equals


    【解决方案1】:

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

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

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

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

    另外,请注意

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

    【讨论】:

    • 谢谢,知道了。同意您因基于 ID 的 equals() 和 hashcode() 可能出现的问题。我想现在我会选择 instanceof 选项,因为在这个阶段在应用程序中引入自然 id 会很困难。
    【解决方案2】:

    我使用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;
    }
    

    【讨论】:

    • 除了你的域类现在几乎不与 Hibernate 耦合。假设您想与其他一些根本不使用休眠的应用程序共享您的域..现在您遇到了麻烦。不要误会我的意思,它会起作用,但它闻起来很糟糕。此外,如果休眠人员决定移动\删除代理工具并且您升级休眠 - 域将不再编译。
    【解决方案3】:

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

    if (HibernateProxyHelper.getClassWithoutInitializingProxy(this) != HibernateProxyHelper.getClassWithoutInitializingProxy(obj)) 
        return false
    

    【讨论】:

    • 请参阅我对拉尔夫回答的评论。您还将域类与 Hibernate 依赖项耦合在一起。
    【解决方案4】:

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

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

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

    【讨论】:

    • 更正了代码格式,根据我的代码,我认为它应该在BaseEntity而不是Object.equals()上调用equals,目前它正在调用Object.equals(),不知道为什么
    • 您能添加 A.getB()(哪种类型?)、BaseEntity.equals() 和 A 的映射(至少 id 和成员 b)的代码吗?也许那时我可以看到更多。
    • 问题是Java后期绑定在BaseEntity中找不到equals()。那么您能否提供 BaseEntity.equals() 的完整代码,包括确切的参数和访问修饰符(public?)。
    猜你喜欢
    • 2012-11-03
    • 1970-01-01
    • 2014-05-03
    • 1970-01-01
    • 2015-10-21
    • 2014-11-18
    • 1970-01-01
    • 2015-07-24
    • 1970-01-01
    相关资源
    最近更新 更多