我了解到,当 Hibernate 为您提供查询结果时,它不会返回您的实际实体类的实例,而是返回一个从您的实际实体类动态子类化的“代理”实例。我理解这种行为的原因,因为它允许实现延迟初始化。但是,关于这些代理类的实现细节,我有几个问题没有得到解答:
equals
中使用该字段怎么办?或 hashCode
方法?执行这些方法是否会导致 NullPointerException
当我以前没有调用过这个领域的 setter/getter 时? 最佳答案
首先,两条规则:
1) 假设您调用
a.equals(b)
其中a
和 b
是同一实体的代理。让我们说equals
方法是这样实现的:public boolean equals(Object other) {
...
if (this.someField.equals(other.someField)) {
...
}
...
}
equals
a
的方法委托(delegate)给目标实例,强制其完全初始化。所以你对 a
中的字段是安全的实例(您可以直接使用它们)。但是,直接访问
b
中的字段实例 ( other.someField
) 是 从不有效的。 b
是否无关紧要是否初始化,代理实例永远不会初始化,只有目标实例。所以,someField
总是 null
在 b
实例。正确的实现是至少对
other
使用 getter实例:this.someField.equals(other.getSomeField())
或保持一致:
this.getSomeField().equals(other.getSomeField())
final
的情况有所不同方法 - Hibernate 不能覆盖它们以将调用委托(delegate)给目标。所以,如果 equals
方法是final
在前面的例子中,你会得到一个 NullPointerException
访问 this.someField
时.所有这些都可以通过配置 Hibernate 使用字节码检测而不是代理来避免,但这有其自身的缺陷并且没有被广泛采用。
2) 同样,它从不初始化代理实例本身。当涉及到目标实例初始化时,这取决于映射中是否定义了字段或属性访问。在这两种情况下都使用反射(在字段访问的情况下将值直接分配给字段,或者在属性访问的情况下调用 setter )。
关于Hibernate:代理实现细节(Lazy Fetching),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32660373/