【问题标题】:Hibernate Lazy Loading, Proxies and InheritanceHibernate 延迟加载、代理和继承
【发布时间】:2012-01-11 16:42:43
【问题描述】:

我在处理继承时遇到了休眠中延迟加载的问题。我有一个实体引用了第二个子类实体。我希望引用延迟加载,但这会导致我的 .equals() 方法出错。

在下面的代码中,如果在 A 的实例上调用 equals(),则在检查 Object o 是否为 C 的实例时,C.equals() 函数中的检查失败。它失败是因为另一个对象是实际上是由 javassist 创建的 Hibernate 代理,它扩展了 B,而不是 C。

我了解 Hibernate 无法在不访问数据库的情况下创建 C 类型的代理,从而中断延迟加载。有没有办法让 A 类中的 getB() 函数返回具体的 B 实例而不是代理(懒惰地)?我尝试在 getB() 方法上使用 Hibernate 特定的 @LazyToOne(LazyToOneOption.NO_PROXY) 注释,但无济于事。

@Entity @Table(name="a")
public class A {
    private B b;

    @ManyToOne(fetch=FetchType.LAZY)
    @JoinColumn(name="b")
    public B getB() {
        return this.b;
    }

    public boolean equals(final Object o) {
        if (o == null) {
            return false;
        }

        if (!(o instanceof A)) {
            return false;
        }
        final A other = (A) o;
        return this.getB().equals(o.getB());
    }
}

@Entity @Table(name="b")
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(
    name="type",
    discriminatorType=DiscriminatorType.STRING
)
public abstract class B {
   private long id;

   public boolean equals(final Object obj) {
       if (this == obj) {
           return true;
       }
       if (obj == null) {
           return false;
       }
       if (!(obj instanceof B)) {
           return false;
       }
       final B b = (B) o;
       return this.getId().equals(b.getId());
    }
}

@Entity @DiscriminatorValue("c")
public class C extends B {
   private String s;

   public boolean equals(Object obj) {
       if (this == obj) {
           return true;
       }
       if (!super.equals(obj)) {
           return false;
       }
       if (obj == null) {
           return false;
       }
       if (!super.equals(obj)) {
           return false;
       }
       if (!(obj instanceof C)) {
           return false;
       }
       final C other = (C) o;
       if (this.getS() == null) {
           if (other.getS() != null) {
               return false;
           }
       } else if (!this.getS().equals(other.getS())) {
           return false;
       }
       return true;
    }
}

@Entity @DiscriminatorValue("d")
public class D extends B {
    // Other implementation of B
}

【问题讨论】:

    标签: java hibernate orm lazy-loading lazy-initialization


    【解决方案1】:

    事实证明,我在尝试使用 @LazyToOne(LazyToOneOption.NO_PROXY) 注释时处于正确的跟踪状态。它对我来说不是开箱即用的,因为我还没有在它上面运行 Hibernate 字节码增强器工具。可在此处找到相关说明:

    19.1.7. Using lazy property fetching

    【讨论】:

    • 有一个使用 NO_PROXY 和检测的选项。您可以在对象外部公开“this”并使用它来检查 instanceof。在“this”中,您将始终拥有未包装且已初始化的对象。这是我的详细解决方案lifeinide.com/post/…
    【解决方案2】:

    您可能想尝试将 getB() 上的 fetch 属性更改为 FetchType.EAGER。希望这会有所帮助。

    【讨论】:

    • 可以,但我希望这个字段延迟加载。
    【解决方案3】:

    无论对象是实体和/或延迟加载的对象,实际上都不可能遵守 equals 的约定并在使用 instanceof 的子类中专门化 equals。实际上,您会遇到b1.equals(c1) == true,但c1.equals(b1) == false

    所以,我认为超类(B)应该定义equals,并使其成为final,因为所有子类都应该使用基类equals方法。

    也就是说,你在 B 中的 equals 方法不对:

    if (!super.equals(obj)) {
       return false;
    }
    

    这意味着equals的Object实现必须返回true才能使两个B实例相等。这意味着两个 B 实例只有在它们是同一个对象时才相等。

    if (!(obj instanceof C)) {
        return false;
    }
    

    B类为什么要检查另一个实例是C的实例。它应该检查另一个实例是否是B的实例。

    因为最后,如果两个 B 具有相同的 ID,则它们是相等的,并且由于 ID 对于所有继承树必须是唯一的,因此如果您将此 equals 方法设为 final,则您是安全的。

    【讨论】:

    • equals 函数中的错误是 type-o's / copy-paste 错误。我纠正了他们。我试图在持久化和不持久化时都使用这些对象,所以我想让 equals 告诉我对象是否相等,而不仅仅是 ID 相等。
    • 那么,您需要在 B 实体中拥有一个功能 ID。此功能 ID 可以由受保护的抽象方法返回:return getFunctionalId().equals(b.getFunctionalId())。但它在整个继承树中也必须是唯一的。
    • 我只是以equals()为例。如果我需要将 a.getB() 强制转换为在外部类中键入 C,我会遇到同样的问题。问题是:我可以从 getB() 而不是代理获得 B 的具体类型。
    • 你不能不使用专有的 Hibernate 代码来解除引用。 Hibernate 建议只将 B 作为 B 访问,使用 B 的多态方法。所以在 B 中声明抽象方法,或者使用访问者模式。
    • 好的,谢谢。你知道@LazyToOne(LazyToOneOption.NO_PROXY) 的目的是什么吗?文档似乎暗示这会做我想要的,但它似乎没有效果。您是否知道任何其他符合 JPA 的 ORM 可能支持此功能?
    猜你喜欢
    • 2021-06-11
    • 2011-07-25
    • 1970-01-01
    • 2011-05-11
    • 1970-01-01
    • 1970-01-01
    • 2020-07-01
    • 2011-09-11
    • 2012-01-24
    相关资源
    最近更新 更多