最终方法是否阻止Hibernate为这样的实体创建代理?

时间:2011-07-07 09:06:30

标签: hibernate

Hibernate使用代理来启用延迟加载集合甚至单端关联。根据Hibernate的(3.6.5)参考文档(第21.1.3节,单端关联代理),如果Hibernate包含“任何最终方法”,则不能构造这样的代理。 ”。
我的问题是,这个限制是仅适用于持久字段的getter / setter还是实际上适用于实体类中的任何方法?那么,像这样的方法是这样的:

public final String toString() {
   return this.getClass().getSimpleName() + id;
}

真的阻止为这个实体创建(CGLIB或Javassist)代理吗? 使用基于字段或属性访问是否重要?由于CGLIB被Javassist取代,这是否会提供这方面的更多功能?

我喜欢在我的实体层次结构中使用继承,因此需要定义一些最终方法, 例如,在基类中防止子类重写这些方法。

提前感谢!

3 个答案:

答案 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时才需要这样做。