强制hibernate在不改变映射的情况下急切地加载多个关联
我有一些实体具有懒惰的一对多关系(为简洁省略了逻辑):
@Entity class A{ @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL) @JoinColumn(name = "a_pk", nullable = false) List blist = new ArrayList(); @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL) @JoinColumn(name = "a_pk", nullable = false) List 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 dlist = new ArrayList(); @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL) @JoinColumn(name = "c_pk", nullable = false) List elist = new ArrayList(); } @Entity class D{ @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL) @JoinColumn(name = "d_pk", nullable = false) List 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
,我会得到相同的exception,但是在line 153
(换句话说,第一个关联被加载,而不是其他关联)。
编辑:Alexey Malev建议为别名设置获取模式; 它看起来确实如此。 符合以下条件:
Criteria c = session .createCriteria(A.class) .add(Restrictions.eq("id", someValue)) .setFetchMode("blist", JOIN) .setFetchMode("clist", JOIN);
我得到了一个不同的exception: 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,...)
我使用的是表名而不是字段名 。
- @Where子句在hibernate join查询中不起作用
- java.math.BigInteger无法强制转换为java.lang.Long
- 通过在某个包中添加所有类,在Hibernate中添加带注释的类。 JAVA
- 检索JPA实体列表和元数据
- Spring 3.2 + Hibernate 4 OpenSessionInViewFilter
- 为什么在Hibernate中使用org.hibernate.TransactionException并避免
- 使用Hibernate回调的优点?
- hibernate锁定等待超时超时;
- org.hibernate.PropertyValueException:not-null属性引用一对一关系中的null或transient值