2011-08-11 51 views
8

我們有與Hibernate 3.3 n + 1個選擇問題。休眠OneToOne自動連接抓取(分辨n + 1個問題)

爲了簡便起見,我只是做了簡短抽象的例子。

假設我們有以下簡單的類:

class MainEntity { 
    @Id 
    public Long id; //we have a table generator create this id 

    @OneToOne (mappedBy ="main") 
    public SubEntity subEntity; 
} 

class SubEntity { 
@Id 
@Column(name = "mainId") //note that this is the same column as the join column below 
public Long mainId; //in order to have the exact same id as the corresponding MainEntity 

@OneToOne (fetch = FetchType.LAZY) 
@JoinColumn (name = "mainId", insertable = false, updatable = false, nullable = false) 
public MainEntity main; //this is used for navigation and queries (" ... subentity.main = :x") 
} 

因此,大家可以看到SubEntity有關係MainEntity由兩個屬性,其中mainId屬性是一個負責管理的關係表達/外鍵。

這工作得很好,完全符合我們的需求。

然而,有一個問題與MainEntity一起熱切加載SubEntity

假設我有一個返回的MainEntity集合的查詢。使用當前設置,Hibernate將發出n + 1個選擇:查詢本身+ n爲每個SubEntity選擇一個。

當然,我可以添加一個join fetch到查詢,但我寧願像Hibernate來自動做。因此我嘗試添加@Fetch(FetchMode.JOIN),但那沒有做任何事情。

我本來還使用@Fetch(FetchMode.SUBSELECT),應減少SELECT語句2沒問題 - 原來的查詢和子實體選擇(至少這是與@CollectionOfElements@Fetch(FetchMode.SUBSELECT)註釋的另一個屬性會發生什麼)。


所以,問題是:我怎麼會告訴Hibernate來自動連接抓取或以熱切加載子實體使用一個選擇?我錯過了什麼嗎?

由於提前,

托馬斯

PS:有一兩件事,可能是一個問題可能是不引用實際的ID列mappedBy = "main",但我不能將其更改爲mappedBy = "id"

回答

6

如果你想MainEntity和子實體之間共享主鍵使用PrimaryKeyJoinColumnMapsId註解。

通過使用PrimaryKeyJoinColumn,通過使用相同的主鍵將MainEntity表與SubEntity表加入,使用相同的主鍵將實體加載 。它應該解決n + 1問題。

MapsId註解讓Hibernate複製從 標識符在我們的例子另一個相關的實體將複製到SubEntity.mainEntity.idSubEntity.id

@Entity 
public class MainEntity { 

    @Id 
    @GeneratedValue(strategy=GenerationType.IDENTITY) 
    @Column(name = "main_Id") 
    private Long id; 

    @OneToOne(cascade = CascadeType.ALL) 
    @PrimaryKeyJoinColumn 
    private SubEntity subEntity ; 
} 


@Entity 
public class SubEntity 
{ 
    @Id @Column(name="main_Id_FK") Long id; 

    @MapsId 
    @OneToOne 
    @JoinColumn(name = "main_Id_FK")  
    @PrimaryKeyJoinColumn 
    private MainEntity mainEntity;   

} 

Hibernate參考文檔:

PrimaryKeyJoinColumn
MapsId

+0

我不知道'@ MapsId',謝謝。我會嘗試一下。即使在n + 1問題不能解決的情況下,我仍然更喜歡這種方法來解決我們當前的問題。只是一個問題:這會讓我實際上只是設置ID而不參考MainEntity?我們有一些情況下缺少創建SubEntity實例,我們只有相應的MainEntity的id。如果可能的話,我寧願不必加載'MainEntity'來設置參考。換句話說:如果'id'有一個值,'mainEntity'是'null',會發生什麼? – Thomas

+0

我只看了一下你鏈接到的'@ MapsId'文檔,不幸的是,這個註釋似乎是在Hibernate 3.5(和JPA 2)中引入的。然而,我們目前堅持使用JBoss 4.2.3(它不支持JPA 2),因此無法使用Hibernate 3.5(我們正在遷移到JBoss 6,但這需要一段時間)。 – Thomas

+0

@Thomas回答你的第一條評論。如何用'MainEntity'的Id創建'SubEntity'。您需要使用'EntityManager.getReference'來獲取對「MainEntity」的引用,而不加載它並將其設置爲SubEntiy.main關係而不加載Main。 –

1

有三個選項,以避免問題的N + 1:

Lot size 

subselect 

Make a LEFT JOIN in the query 

Here FAQ1 Here FAQ2

+0

兩個鏈接導致我在同一個頁面,任何更具體的鏈接?此外:'批量大小':我想你的意思是'批量大小' - 在這裏不起作用,'subselect' - 我試過,但如前所述,它沒有工作,'左連接' - 我寧願做一個提取連接,但如前所述,我不想將它添加到每個查詢中。 – Thomas