2012-01-11 38 views
3

我在處理繼承時遇到了休眠中延遲加載的問題。我有一個實體引用了第二個子類的實體。我想要引用來加載懶惰,但這會導致我的.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 
} 

回答

1

事實證明,我正在嘗試使用@LazyToOne(LazyToOneOption.NO_PROXY)註釋進行正確的跟蹤。由於我還沒有在其上運行Hibernate字節碼增強器工具,所以它並不適合我開箱即用。說明這可以在這裏找到:

19.1.7. Using lazy property fetching

+0

有一個選項可以使用NO_PROXY和儀器。你可以在對象之外暴露「this」並用它來檢查instanceof。在「這個」中,你總是會打開並且已經初始化的對象。這裏是我的詳細解決方案http://lifeinide.com/post/2017-05-28-hibernate-single-side-associations-lazy-fetch/ – 2017-05-28 11:50:54

0

你可能想嘗試改變對getB()的fetch屬性FetchType.EAGER。希望這可以幫助。

+0

這將做到這一點,但我想這個領域是延遲加載。 – sutlermb 2012-01-11 17:53:29

0

無論對象是實體還是惰性加載,實際上都不可能尊重equals的約定,並且在使用instanceof的子類中擁有equals的專精。事實上,如果你有b1.equals(c1) == true,但是c1.equals(b1) == false,你會處於這種狀態。所以,我認爲超類(B)應該定義等於,並使其成爲最終的,因爲所有的子類都應該使用基類equals方法。

這就是說,在B您的equals方法是不正確的:

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

這意味着平等的對象實現必須返回true有平等的兩個B實例。這意味着如果兩個B實例是同一個對象,那麼它們只相等。

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

爲什麼的B級檢查,其他實例是它應該檢查是否另一個實例是

由於在結束時,兩個B是相等的,如果B的一個實例C的一個實例它們具有相同的ID,並且由於ID對於所有繼承樹必須是唯一的,因此如果您使此equals方法最終成功,那麼您是安全的。

+0

等號函數中的錯誤是type-o's/copy-paste錯誤。我糾正了他們。我試圖在持久和不持久時使用這些對象,所以我想等於告訴我對象是否相等,而不僅僅是ID相等。 – sutlermb 2012-01-11 16:59:16

+0

那麼,你需要在B實體中擁有一個功能性ID。此功能ID可以通過受保護的抽象方法返回:return getFunctionalId()。equals(b.getFunctionalId())。但是它在整個繼承樹中也必須是唯一的。 – 2012-01-11 17:08:22

+0

我剛剛用equals()作爲例子。如果我需要將a.getB()轉換爲在外部類中鍵入C,我將遇到同樣的問題。問題是:我可以從getB()獲取具體的B類型,而不是代理。 – sutlermb 2012-01-11 18:08:24