Hibernate:代理实现细节(Lazy Fetching)

标签 hibernate proxy fetch lazy-evaluation

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

  • 只有当我使用 getter 时才会加载延迟获取的字段吗?如果我在我的 equals 中使用该字段怎么办?或 hashCode方法?执行这些方法是否会导致 NullPointerException当我以前没有调用过这个领域的 setter/getter 时?
  • 当它的初始化被触发时,Hibernate 究竟是如何初始化该字段的?它是执行我在实体类中定义的字段的 setter 方法,还是会通过反射或类似的方式将值直接分配给变量?
  • 最佳答案

    首先,两条规则:

  • 代理委托(delegate)全部 对目标实例的非 final方法调用,如果在映射中定义了对 id 的属性访问,则获取 id 的方法除外。
  • 代理对象实例为 从不初始化,目标实例被初始化。

  • 1) 假设您调用 a.equals(b)其中ab是同一实体的代理。让我们说equals方法是这样实现的:
    public boolean equals(Object other) {
      ...
      if (this.someField.equals(other.someField)) {
        ...
      }
      ...
    }
    
    equals a的方法委托(delegate)给目标实例,强制其完全初始化。所以你对 a 中的字段是安全的实例(您可以直接使用它们)。

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

    正确的实现是至少对 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/

    相关文章:

    java - Spring Data JPA 存储库抛出空指针

    java - Hibernate 查询抛出

    proxy - Tinyproxy 反向代理 "Access denied"

    database - 无法从数据库表中获取消息。 ErrorException 说 :count(): Parameter must be an array or an object that implements Countable

    mysql - 单表继承和字段列表中的未知列

    java - Hibernate @JoinColumnOrFormula 错误

    javascript - React.js : Proxy that I set in package. json 在 webpack 中不起作用

    java - 使用 ANT 设置代理

    c# - 为什么多边形二进制图像在 MySQL 服务器上的大小与应用程序中获取的结果不同?

    xcode - Swift Cloudkit 获取当前用户信息以供整个应用程序使用