簡體   English   中英

避免n + 1渴望獲取子集合元素關聯

[英]Avoiding n+1 eager fetching of child collection element association

我有以下課程:

@Entity
@Table(name = "base")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "DISCRIMINATOR", discriminatorType = DiscriminatorType.STRING)
@ForceDiscriminator
public class Base {
    // ...
}

@Entity
@DiscriminatorValue("foo")
public class Foo extends Base {
    @OneToMany( mappedBy = "foo", cascade=CascadeType.ALL )
    private List<Bar> bars = new ArrayList<Bar>();

    // ...
}

@Entity
public class Bar {
    @ManyToOne (optional = false)
    @JoinColumn(name = "foo_id" )
    private Foo foo;

    @OneToOne
    @JoinColumn(name = "baz_id", nullable = false)
    private Baz baz;

    //...
}

@Entity
public class Baz {
    // ...
}

現在我基本上想要加載所有Base ,但是在適用時需要加載吧,所以我使用以下查詢:

SELECT b FROM Base b LEFT JOIN FETCH b.bars

雖然這有效,但似乎為Bar實體生成了SELECT N + 1問題:

Hibernate: /* SELECT b FROM Base b LEFT JOIN FETCH b.bars */ SELECT ...
Hibernate: /* load com.company.domain.Baz */ SELECT ...
Hibernate: /* load com.company.domain.Baz */ SELECT ...

是否有可能告訴hibernate急切地為子集合中的每個元素加載一個關聯而不訴諸N + 1 SELECT?

我嘗試了以下查詢的內容,這顯然不起作用,因為它的集合:

SELECT b FROM Base b LEFT JOIN FETCH b.bars LEFT JOIN FETCH b.bars.baz
//Results in: illegal attempt to dereference collection [Foo.id.bars] with element property reference [baz]

我也嘗試使用IN(b.bars) bars ,雖然這允許我引用IN(b.bars) bars ,但它似乎並不急於加載我的目標吧集合。

解釋為什么會發生這種情況也很好,因為我似乎無法弄明白。

如果你想用out(n + 1)選擇檢索Bar和Baz,請使用以下hql。

SELECT b FROM Base b LEFT JOIN FETCH b.bars bar LEFT JOIN FETCH bar.baz

這應該只導致一個sql。

此外,如果您不想獲取'Baz',只需從Bar-> Baz'懶惰'進行關聯。

默認情況下,JPA強制'eager'獲取'@OneToOne'和'@ManyToOne'關聯。 所以,你必須明確地使它變得懶惰,如下所示。

@Entity
public class Bar {

    @OneToOne
    @JoinColumn(name = "baz_id", nullable = false, fetch=FetchType.Lazy)
    private Baz baz;

    //...
}

我的方法(我有一個有限的,穩定數量的二級實體)

首先,讓所有Bars進入會話:

 SELECT bar FROM Bar bar

之后,所有Bar實體都將位於緩存中,您的查詢將無需其他實體即可訪問它們。

我想說在這種情況下,獲取策略的改變可能會有所幫助。 文件說:

http://docs.jboss.org/hibernate/orm/4.1/manual/en-US/html_single/#performance-fetching-batch

提取:

您還可以啟用批量提取集合。 例如,如果每個Person都有一個懶惰的Cats集合,並且當前在Session中加載了10個人,則遍歷所有人將生成10個SELECT,每次調用getCats()一個。 如果在Person的映射中為cats集合啟用批量提取,Hibernate可以預取集合:

<class name="Person">
    <set name="cats" batch-size="3">
        ...
    </set>
</class>

批量大小為3時,Hibernate將在四個SELECT中加載3個,3個,3個,1個集合。 同樣,屬性的值取決於特定會話中未初始化集合的預期數量。

我正在使用它並且效果很好。 如果我正在分頁並且總是只選擇20條記錄,則批量大小=“20”的效果很好。 如果我需要超過20,仍然會減少對DB的調用

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM