enl*_*ait 9 java hibernate lazy-loading
我有一些实体具有懒惰的一对多关系(为简洁省略了逻辑):
@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{
}
Run Code Online (Sandbox Code Playgroud)
在某些(非常罕见的)情况下,我想急切地加载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
Run Code Online (Sandbox Code Playgroud)
当我尝试访问东西时,我得到一个例外line 152:
org.hibernate.LazyInitializationException:
failed to lazily initialize a collection of role:
A.blist, could not initialize proxy - no Session
Run Code Online (Sandbox Code Playgroud)
如果我将获取策略更改JOIN为条件中的任何位置,我会得到相同的异常,但是line 153(换句话说,第一个关联被加载,而不是其他的).
编辑:Alexey Malev建议为别名设置提取模式; 它看起来确实如此.符合以下条件:
Criteria c = session
.createCriteria(A.class)
.add(Restrictions.eq("id", someValue))
.setFetchMode("blist", JOIN)
.setFetchMode("clist", JOIN);
Run Code Online (Sandbox Code Playgroud)
我得到了一个不同的异常: org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bags 在第149行.因此,加入取不是一个选项.
问题是,我如何加载整个东西?
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问题的一个明显例子,但我可以忍受.
| 归档时间: |
|
| 查看次数: |
13416 次 |
| 最近记录: |