簡體   English   中英

連接的高效Hibernate標准返回許多部分重復

[英]Efficient Hibernate criteria for join returning many partial duplicates

我正在獲取一長串實體,這些實體引用其他引用的實體......最后,通常所有實體都將單個user稱為其owner 並不奇怪,因為所查詢的是屬於單個user實體。 許多行中有更多的部分重復; 實際上,只有一小部分是唯一數據。 由於查詢似乎很慢,我雖然可以通過單獨使用來獲取一些東西

criteria.setFetchMode(path, FetchMode.SELECT);

這適用於我的上述情況,但是當查詢許多用戶(作為管理員)時,它變得很糟糕,因為hibernate為每個 user發出單獨的查詢,而不是像

SELECT * FROM User WHERE id IN (?, ?, ..., ?)

或者根本不提取它們 (不能比每個實體的一個查詢更糟糕)。 我想知道我錯過了什么?

因此,我沒有獲取大量冗余數據,而是遇到了1 + N問題,顯然會有1 + 1個查詢。

  • 有沒有辦法指示Hibernate使用正確的查詢?
  • 有沒有辦法阻止Hibernate通過在條件本身中指定它來獲取所有者(而不是在字段上放置fetch=FetchType.LAZY ;懶惰應該是查詢特定的)?

我認為這不重要,但我的課程就像

class Child {
    @ManyToOne Father father;
    @ManyToOne Mother mother;
    ...
}
class Father {
    @ManyToOne User owner;
    ...
}
class Mother {
    @ManyToOne User owner;
    ...
}

而查詢就像

createCriteria(Child.class)
.add(Restrictions.in("id", idList))
.add(Restrictions.eq("isDeleted", false))

.createAlias("Father", "f")
.add(Restrictions.eq("f.isDeleted", false))
.setFetchMode("f.owner", FetchMode.SELECT)

.createAlias("Mother", "m")
.add(Restrictions.eq("m.isDeleted", false))
.setFetchMode("m.owner", FetchMode.SELECT)

.list();

重要的是, owner不會被使用並且可以被代理。 FetchMode.SELECT的javadoc說

使用單獨的選擇急切地獲取

所以它基本上承諾我想要的1 + 1查詢,而不是“每個實體使用單獨的選擇”。

獲取配置文件旨在幫助您實現所需,但目前非常有限,您只能使用連接樣式獲取配置文件覆蓋默認獲取計划/策略(您可以使懶惰關聯渴望,但不是副反之亦然)。 但是,您可以使用此技巧來反轉該行為:默認情況下使關聯變為惰性,並默認為所有會話/事務啟用配置文件。 然后在要延遲加載的事務中禁用配置文件。

恕我直言,上面的解決方案看起來太麻煩了,我在大多數用例中使用的方法,以避免加載冗余數據和N + 1選擇問題是使關聯變得懶惰並定義批量大小

我寫了一個小項目來演示這種行為。 根據您的條件生成的SQL如下:

select
    this_.id as id1_0_4_,
    this_.father_id as father_i3_0_4_,
    this_.isDeleted as isDelete2_0_4_,
    this_.mother_id as mother_i4_0_4_,
    f1_.id as id1_1_0_,
    f1_.isDeleted as isDelete2_1_0_,
    f1_.owner_id as owner_id3_1_0_,
    user5_.id as id1_3_1_,
    user5_.isDeleted as isDelete2_3_1_,
    m2_.id as id1_2_2_,
    m2_.isDeleted as isDelete2_2_2_,
    m2_.owner_id as owner_id3_2_2_,
    user7_.id as id1_3_3_,
    user7_.isDeleted as isDelete2_3_3_ 
from
    Child this_ 
inner join
    Father f1_ 
        on this_.father_id=f1_.id 
left outer join
    User user5_ 
        on f1_.owner_id=user5_.id 
inner join
    Mother m2_ 
        on this_.mother_id=m2_.id 
left outer join
    User user7_ 
        on m2_.owner_id=user7_.id 
where
    this_.id in (
        ?, ?
    ) 
    and this_.isDeleted=? 
    and f1_.isDeleted=? 
    and m2_.isDeleted=?
  1. 更改條件API中的FetchMode不會影響查詢。 仍然查詢所有者數據。
  2. Ids在“in”子句中,Hibernate沒有為每個Id發出單獨的查詢。

如其他答案中所述,如果實體關系設置為EAGER ,即JPA默認值,則無法更改Criteria API中的提取模式。 需要將獲取模式更改為LAZY

你可以在這里看到它

總結一下我的挫敗感......在這方面,Hibernate充滿了驚喜(錯誤?):

  • 除非使用@ManyToOne(fetch=FetchType.LAZY)聲明屬性,否則無法更改任何內容
  • 默認值是FetchType.EAGER ,這是愚蠢的,因為它無法被覆蓋
  • 使用criteria.setFetchMode(path, FetchMode.SELECT)是沒有意義的,因為它總是一個無操作(要么被忽略,因為屬性的不可覆蓋的熱切或屬性已經是懶惰的)!
  • 默認情況下,懶惰地獲取1 + N問題
  • 它可以通過類級別的@BatchSize注釋來控制
  • 在標量字段上放置@BatchSize注釋會被靜默忽略

為了得到我想要的東西(兩個SQL查詢),我只需要兩件事:

  • 使用@ManyToOne(fetch=FetchType.LAZY)聲明屬性@ManyToOne(fetch=FetchType.LAZY)
  • @BatchSize(size=aLot)放在屬性的類上

這很簡單,但有點難以找到(因為上面所有被忽略的事情)。 我還沒有查看過fetch配置文件。

除非使用@ManyToOne(fetch=FetchType.LAZY)聲明屬性,否則無法更改任何內容

確實,至少在目前,直到獲取配置文件功能被擴展為提供將預先加載更改為延遲的能力。

默認值是FetchType.EAGER ,這是愚蠢的,因為它無法被覆蓋

是的,我同意它很糟糕,但在Hibernate本機API中,默認情況下一切都是懶惰的; 除非另有明確規定,否則JPA要求一個協會渴望。

使用criteria.setFetchMode(path,FetchMode.SELECT)是沒有意義的,因為它總是一個無操作(要么被忽略,因為屬性的不可覆蓋的熱切或屬性已經是懶惰的)!

有了它,你應該能夠覆蓋其他懶惰的獲取模式。 HHH-980此評論約javadoc的混亂導致Hibernate的貢獻者之一。

默認情況下,懶惰地獲取1 + N問題

它與延遲加載無關,如果你沒有在同一個查詢中獲取急切加載的關聯,它也是預先加載的默認值。

它可以通過類級別的@BatchSize注釋來控制

您必須將它放在類級別才能使它與該實體的一個關聯生效; 這個答案很有幫助。 對於集合關聯(與其他實體中定義的實體的多對多關聯),您可以靈活地為每個關聯單獨定義它。

暫無
暫無

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

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