2012-11-14 41 views
5

我的問題是,Hibernate加載OneToOne關聯對每個null關係執行+1選擇。Hibernate OneToOne(可選= true)與FetchMode.JOIN嘗試重新選擇空值

實體例如:

@Entity 
class SideBlue { 
    @Column(nullable = false) 
    private Integer timestamp; 

    @OneToOne(optional=true) 
    @JoinColumn(name="timestamp", referenceColumn="timestamp", insertable = false, updatable = false) 
    SideRed redSide; 
} 
@Entity 
class SideRed { 
    @Column(nullable = false) 
    private Integer timestamp; 
} 

(這是一個傳統的數據庫模式,所以對數據庫的修改是不允許的)

查詢例如:

CriteriaBuilder builder... CriteriaQuery query... 
Root<SideBlue> root = query.from(SideBlue.class); 
root.fetch(SideBlue_.sideRed, JoinType.LEFT); 
entityManager().createQuery(query).getResultList(); 

結果: 如果所有的藍方實體有一個紅色的一面,一切正常,所以休眠只對數據庫執行一個查詢,以檢索任何實體。

但是,如果藍色邊實體沒有關聯紅色邊實體,hibernate會嘗試再次找到另一邊。對於每個null redSide屬性,Hibernate sql註釋說'/ *加載RedSide */select ...'。

如何跳過這第二個選擇?

實際問題出現在延遲不是非常低的時候。如果我嘗試選擇100萬行,並且1/3有空的「紅色側」,則總延遲時間是一個真正的問題。

編輯:

這是調試日誌查詢

10:04:32.812 [main] DEBUG org.hibernate.loader.Loader - Result set row: 0 
10:04:32.815 [main] DEBUG org.hibernate.loader.Loader - Result row: EntityKey[SideBlue#1269721], EntityKey[SideRed#3620564] 
10:04:32.833 [main] DEBUG org.hibernate.loader.Loader - Result set row: 1 
10:04:32.833 [main] DEBUG org.hibernate.loader.Loader - Result row: EntityKey[SideBlue#1269776], null 

第一行包含了藍色和紅色的邊,但第二個只藍方。所以冬眠必須知道相關的紅邊不存在。但是,所有結果行處理...

10:04:33.083 [main] DEBUG o.h.engine.internal.TwoPhaseLoad - Resolving associations for [BlueSide#1269721] 
10:04:33.084 [main] DEBUG org.hibernate.loader.Loader - Loading entity: [RedSide#component[timestamp]{timestamp=1338937390}] 
10:04:33.084 [main] DEBUG org.hibernate.SQL - /* load RedSide */ select ... 
! Nothing really loaded because the previous SQL return empty result set, again ! 
10:04:33.211 [main] DEBUG org.hibernate.loader.Loader - Done entity load 
+1

你的例子很讓人困惑,它上面有@JoinColumn標註的一面根據定義是關係的所有者。 – Affe

+0

我的錯誤。我重新定義/改進了實體定義。這是我實體的簡化版本,但我認爲這已經足夠了。數據庫在雙方時間戳之間沒有FK。 –

回答

0

好後,你想,當你做SideBlue查詢不加載SideRed。我認爲這是一個延遲加載的問題,與這種「限制」有關從休眠(從https://community.jboss.org/wiki/SomeExplanationsOnLazyLoadingone-to-one?_sscc=t):

class B { 
    private C cee; 

    public C getCee() { 
     return cee; 
    } 

    public void setCee(C cee) { 
     this.cee = cee; 
    } 
} 

class C { 
    // Not important really 
} 

右側裝B之後,你可以調用getCee()來獲取C.但是你看, getCee()是YOUR類的一種方法,Hibernate無法控制它。 Hibernate不知道什麼時候有人打電話給getCee()。 這意味着Hibernate必須在從數據庫加載B的時刻將適當的值放入「cee」屬性 。

如果爲C啓用代理, Hibernate可以將尚未加載的C代理對象,但當有人使用它時將加載 。這可以一次性爲 提供延遲加載。

但是,現在想象一下,您的B對象可能有或沒有關聯的C(constrained =「false」) 。當 具體B沒有C時,應該得到的是什麼()返回?空值。但請記住,Hibernate必須在設置B時設置「cee」的正確值 (因爲當有人調用getCee()時,它不知道 )。代理在這裏沒有幫助,因爲 在已經非空的對象中代理自己。所以簡歷:如果你的B-> C 映射是強制性的(constrained = true),那麼Hibernate將使用代理的 C導致延遲初始化。但是,如果你允許B沒有C, Hibernate只是在加載B時檢查C的存在。 但是SELECT檢查存在效率不高,因爲同樣的SELECT可能不僅檢查存在,而且加載整個對象。所以懶惰 加載消失。