Hibernate使用代理来启用延迟加载集合甚至单端关联。根据Hibernate的(3.6.5)参考文档(第21.1.3节,单端关联代理),如果Hibernate包含“任何最终方法”,则不能构造这样的代理。 ”。
我的问题是,这个限制是仅适用于持久字段的getter / setter还是实际上适用于实体类中的任何方法?那么,像这样的方法是这样的:
public final String toString() {
return this.getClass().getSimpleName() + id;
}
真的阻止为这个实体创建(CGLIB或Javassist)代理吗? 使用基于字段或属性访问是否重要?由于CGLIB被Javassist取代,这是否会提供这方面的更多功能?
我喜欢在我的实体层次结构中使用继承,因此需要定义一些最终方法, 例如,在基类中防止子类重写这些方法。
提前感谢!
答案 0 :(得分:6)
在Hibernate邮件列表的帮助下(感谢Emmanuel Bernardt!)我能够回答我自己的问题,摘要是:
最终方法不会阻止Hibernate一般创建代理,但除非这些方法不使用实体的任何状态,否则这是非常不可取的。
一些背景信息:Hibernate既不使用cglib也不使用字节码增强,因此,为了让代理懒惰地初始化其目标实体,它必须拦截任何可能使用该目标实体的状态的方法。 现在完全可以拥有像这样的最终方法
public final doSomething(String a, Integer b ) {
// do complicated stuff using only a and b (no instance members accessed!)
}
但是只要此方法直接使用任何持久字段或通过另一个实例方法,这将绕过代理,从而导致意外行为。
作为旁注,这与您不应直接访问其他实例的字段的原因相同,例如在实体equals
方法中:
// XXX bad code!
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Profile)) return false;
Profile profile = (Profile) o;
// XXX this bypasses a possible proxy, use profile.getName() instead!
return (name == null ? profile.name == null : name.equals(profile.name));
}
答案 1 :(得分:1)
我很确定,正如参考文献所述,它适用于任何方法。实际上,在我的理解中,代理只不过是实体的子类,除了最初的实体ID之外没有任何状态,并且一旦初始化,它将每个方法调用委托给实体类的实际实例。因此,它必须覆盖所有方法才能
答案 2 :(得分:0)
受这个问题的启发,我为Intellij IDEA创建了一个插件,解决了这个问题。这是:
https://plugins.jetbrains.com/plugin/7866
简单描述如下:
Hibernate在某些情况下默默地失败,导致错误 难以追查。这个插件有助于查找和修复 其中一些问题。在设置>下检查>过冬 检查它增加了以下检查: •坚持上课是最后的; •持久化类的最终方法使用直接字段访问。
============================================== < / p>
附注:
正如@BartvanHeukelom在评论中所说,最终方法无法代理的事实可以用作资产:你可以那么 从getter获取实体的id,而不初始化代理并加载其字段。这是我使用的代码:
@SuppressWarnings ("AccessingFieldFromAFinalMethodOfPersistedClass")
public final Id getId() {
if (this instanceof HibernateProxy) return (Id)((HibernateProxy)this).getHibernateLazyInitializer().getIdentifier();
else return id;
}
请注意@SuppressWarnings
。只有在我的插件中使用IntelliJ IDEA时才需要这样做。