Hibernate使用代理来支持集合的延迟加载,甚至是单端关联。根据Hibernate(3.6.5)的参考文档(* 第21.1.3节,单端关联代理 *),如果代理包含“any final methods",那么Hibernate就不能构造这样的代理。
我的问题是,这个限制只适用于持久化字段的getter/setter,还是真的适用于实体类中的任何方法?所以,像这样的一个方法:
public final String toString() {
return this.getClass().getSimpleName() + id;
}
是否真的阻止为该实体创建(CGLIB或Javassist)代理?使用基于字段的访问还是使用属性访问有关系吗?由于CGLIB被Javassist取代,这是否在这个方向上提供了更多的功能?
我喜欢在实体层次结构中使用继承,因此需要定义一些最终方法,例如,在基类中,以防止子类重写这些方法。
先谢谢你!
4条答案
按热度按时间umuewwlo1#
在Hibernate邮件列表的帮助下(感谢Emmanuel Bernardt!我能回答我自己的问题,总结如下:
Final方法一般不会阻止Hibernate创建代理,但除非这些方法不使用实体的任何状态,否则这是非常不可取的。
一些背景资料:Hibernate既不使用cglib也不使用Javassist,所以为了让代理延迟初始化其目标实体,它必须拦截任何可能使用该目标实体状态的方法。现在有一个这样的最终方法是完全可以的
但是一旦该方法直接或通过另一示例方法使用任何持久字段,这将绕过代理,从而导致意外行为。
作为旁注,这也是为什么你不应该直接访问其他示例的字段的原因,例如在实体
equals
方法中:fiei3ece2#
我很确定,正如参考文献所说,它适用于任何方法。实际上,在我的理解中,代理只不过是实体的子类,除了最初的实体ID之外没有状态,并且一旦初始化,它就将每个方法调用委托给实体类的实际示例。因此,它必须重写所有方法,以便
gkn4icbw3#
受这个问题的启发,我为Intellij IDEA创建了一个插件,解决了这个问题。这就是:
https://plugins.jetbrains.com/plugin/7866
简单的描述是这样的:
Hibernate在某些情况下会默默地失败,导致难以追踪的bug。这个插件可以帮助查找和修复其中的一些问题。在设置>检查>休眠检查下,添加以下检查:·持久化类是final;·持久化类的Final方法使用直接字段访问。
附说明:
正如@BartvanHeukelom在评论中所说的那样,最终方法不能被代理的事实可以被用作一种资产:然后,您可以从getter获取实体的id,而无需初始化代理并加载其字段。这是我用途:的代码
请注意
@SuppressWarnings
。这是必要的,只有当你使用IntelliJ IDEA与我的插件。ie3xauqp4#
提出了一个问题:最后一个方法是否会阻止Hibernate创建代理-不,它不会。虽然在构建
ProxtFactory
时,当发现最终方法时,Entity Tuplizer会发出The HHH000112: Getters of lazy classes cannot be final
错误消息,但Hibernate会继续执行,并且不会标记此有问题的方法。这是因为代理机制,
org.hibernate.proxy.ProxyFactory
接口的实现,是开放的配置,开发人员可以自由选择它的任何实现。默认的Hibernate代理工厂,基于Javasist,确实不会拦截final
方法,但是开发人员可以实现一个自定义的代理工厂,它不仅可以“覆盖”final方法,而且可以使用字节码库(如CGLib,ByteBuddy甚至ASM本身)做许多其他事情。要覆盖默认的Hibernate
ProxyFactory
,从Hibernate 5.3.9开始,开发人员可以在META-INF/services/org.hibernate.integrator.spi.Integrator
类路径的文件中注册org.hibernate.integrator.spi.Integrator
的自定义实现:这个
Itegrator
将注册自定义EntityTuplizer
反过来,它会注册一个定制的代理工厂,
请注意,可以重用大多数复杂的Hibernate代理逻辑,因为
ProxyFactory
的实现可能(而且可能应该)非常简单,因为org.hibernate.proxy.LazyInitializer
的所有实现负担都可以委托给BasicLazyInitializer
的抽象类BasicLazyInitializer
,您的LazyInitializer
实现可能会从该抽象类派生,并且您必须实现的只是代理引擎依赖的逻辑。如果JPA层构建在Hibernate之上,则上述所有内容都适用于JPA层。