簡體   English   中英

強制hibernate在不改變映射的情況下急切地加載多個關聯

[英]Force hibernate to eagerly load multiple associations without changing mapping

我有一些實體具有懶惰的一對多關系(為簡潔省略了邏輯):

@Entity
class A{
    @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @JoinColumn(name = "a_pk", nullable = false)
    List<B> blist = new ArrayList<>();

    @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @JoinColumn(name = "a_pk", nullable = false)
    List<C> clist = new ArrayList<>();

    @Column(name = "natural_identifier", nullable = false)
    private String id;
}

@Entity
class B{
}

@Entity
class C{
    @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @JoinColumn(name = "c_pk", nullable = false)
    List<D> dlist = new ArrayList<>();

    @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @JoinColumn(name = "c_pk", nullable = false)
    List<E> elist = new ArrayList<>();
}

@Entity
class D{
    @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @JoinColumn(name = "d_pk", nullable = false)
    List<F> flist = new ArrayList<>();
}

@Entity
class E{
}

@Entity
class F{
}

在某些(非常罕見的)情況下,我想急切地加載A的實例及其所有關聯。 原因是我希望對該A實例進行一些修改,並將它作為一個整體,然后根據用戶輸入保存或丟棄它們。

如果我要根據需要加載內容,我必須根據用戶請求將實體重新附加到新會話,但是它們正在被修改,並且修改不應該保留。

所以我寫了類似的東西:

Session session = sessionFactory.openSession();
s.beginTransaction();
Criteria c = session
                    .createCriteria(A.class)
                    .add(Restrictions.eq("id", someValue))
                    .setFetchMode("blist", SELECT)
                    .setFetchMode("clist", SELECT)
                    .createAlias("clist", "c")
                    .setFetchMode("c.dlist", SELECT)
                    .setFetchMode("c.elist", SELECT)
                    .createAlias("c.dlist", "d")
                    .setFetchMode("d.flist", SELECT);
A a = (A) c.uniqueResult();
session.close(); // line 150

a.getBlist().size(); // line 152 - debug
a.getClist().size(); // line 153 - debug

當我嘗試訪問東西時,我在line 152得到一個例外:

org.hibernate.LazyInitializationException:
failed to lazily initialize a collection of role: 
A.blist, could not initialize proxy - no Session

如果我將標記中的獲取策略更改為JOIN ,我會得到相同的異常,但是在line 153 (換句話說,第一個關聯被加載,而不是其他關聯)。

編輯:Alexey Malev建議為別名設置提取模式; 它看起來確實如此。 符合以下條件:

Criteria c = session
                    .createCriteria(A.class)
                    .add(Restrictions.eq("id", someValue))
                    .setFetchMode("blist", JOIN)
                    .setFetchMode("clist", JOIN);

我得到一個不同的異常: org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bags 在第149行上 org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bags 。因此,join-fetching不是一個選項。

問題是,我如何加載整個東西?

Hibernate版本4.2.12.Final

我通過使用稍微不同的方法找到了解決原始問題的方法。 引用Hibernate ORM文檔:

有時需要在關閉Session之前初始化代理或集合。 例如,您可以通過調用cat.getSex()或cat.getKittens()。size()來強制初始化。 但是,這可能會使代碼的讀者感到困惑,並且對於通用代碼來說不方便。

靜態方法Hibernate.initialize()和Hibernate.isInitialized()為應用程序提供了一種使用延遲初始化集合或代理的便捷方式。 只要其Session仍處於打開狀態,Hibernate.initialize(cat)將強制執行代理cat的初始化。 Hibernate.initialize(cat.getKittens())對小貓集合有類似的效果。

簡單地獲取延遲集合( a.getBlist() )不會使它加載 - 我最初犯了這個錯誤。 如果我嘗試從該集合中獲取一些數據(獲取項目,獲取集合大小),它將加載。 在該集合上調用Hibernate.initialize(..)也會這樣做。

因此,迭代實體關聯及其各自的關聯等,並在會話中顯式初始化它們(例如,使用Hibernate.initialize() )將在會話關閉后加載會話之外的所有內容。

這種方法根本不使用准則提取模式(為什么它們不能像記錄的那樣工作另一個問題)。

這是N + 1問題的一個明顯例子,但我可以忍受。

我認為你使用了錯誤的獲取模式。 你最有可能需要JOIN 試試這個:

Criteria c = session
                .createCriteria(A.class)
                .add(Restrictions.eq("id", someValue))
                .setFetchMode("blist", JOIN)
                .setFetchMode("clist", JOIN)
                .createAlias("clist", "c")
                .setFetchMode("c", JOIN)
...//the same for others

注意 - 我也為別名添加了獲取模式。 當您無法加載任何具有別名的列表時的行為會導致猜測。

為了記錄我有一個類似的問題,因為鍵值setFecthMode(key,...)
我使用的是表名而不是字段名

暫無
暫無

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

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