【问题标题】:Hibernate: Details of Proxy Implementation (Lazy Fetching)Hibernate:代理实现细节(Lazy Fetching)
【发布时间】:2015-09-18 20:18:32
【问题描述】:

我了解到,当 Hibernate 为您提供查询结果时,它不会返回您的实际实体类的实例,而是返回一个从您的实际实体类动态子类化的“代理”实例。我理解这种行为的原因,因为它允许实现延迟初始化。但是,关于这些代理类的实现细节,我还有几个问题没有得到解答:

  1. 只有在我使用 getter 时才会加载延迟获取的字段吗?如果我在我的equalshashCode 方法中使用该字段怎么办?当我之前没有调用该字段的getter时,这些方法的执行是否会导致NullPointerException

  2. 当字段初始化被触发时,Hibernate 究竟是如何初始化字段的?它是执行我在实体类中定义的字段的 setter 方法,还是会通过反射或类似的方式将值直接分配给变量?

【问题讨论】:

    标签: hibernate proxy fetch lazy-evaluation


    【解决方案1】:

    首先,两条规则:

    1. 代理将所有非最终方法调用委托给目标实例,如果在映射中定义了对 id 的属性访问,则获取 id 的方法除外。
    2. 代理对象实例从不初始化,目标实例已初始化。

    1)假设您调用a.equals(b),其中ab 都是同一实体的代理。假设equals 方法是这样实现的:

    public boolean equals(Object other) {
      ...
      if (this.someField.equals(other.someField)) {
        ...
      }
      ...
    }
    

    aequals 方法被委托给目标实例,强制其完全初始化。所以你对a 实例中的字段是安全的(你可以直接使用它们)。

    但是,直接访问 b 实例 (other.someField) 中的字段从不有效。 b 是否初始化无关紧要,代理实例永远不会初始化,只有目标实例。所以,someFieldb 实例中始终是null

    正确的实现是至少对other 实例使用getter:

    this.someField.equals(other.getSomeField())
    

    或保持一致:

    this.getSomeField().equals(other.getSomeField())
    

    final 方法的情况有所不同——Hibernate 无法覆盖它们以将调用委托给目标。因此,如果equals 方法在上一个示例中为final,则在访问this.someField 时会得到NullPointerException

    所有这些都可以通过将 Hibernate 配置为使用字节码检测而不是代理来避免,但这有其自身的缺陷并且没有被广泛采用。

    2) 同样,它从不初始化代理实例本身。当涉及到目标实例初始化时,这取决于映射中是否定义了字段或属性访问。在这两种情况下都使用反射(在字段访问的情况下直接将值分配给字段,或者在属性访问的情况下调用设置器)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-06-05
      • 1970-01-01
      • 2015-04-14
      • 1970-01-01
      • 2016-10-04
      • 1970-01-01
      相关资源
      最近更新 更多