我有一些实体具有一对一的懒惰关系(为简洁起见,省略了逻辑):
@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:
line 152
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: A.blist, could not initialize proxy - no Session
如果将获取策略更改JOIN为条件中的所有位置,都会遇到相同的异常,但是会出现异常line 153(换句话说,第一个关联会被加载,而其他则不会加载)。
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行。因此,无法进行join-fetching。
org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bags
问题是,如何装载整个东西?
Hibernate 4.2.12版最终版
通过使用稍微不同的方法,我找到了解决原始问题的方法。引用Hibernate ORM文档:
有时,需要在关闭会话之前初始化代理或集合。例如,您可以通过调用cat.getSex()或cat.getKittens()。size()来强制初始化。但是,这可能会使代码的读者感到困惑,并且对于通用代码而言并不方便。 静态方法Hibernate.initialize()和Hibernate.isInitialized()为应用程序提供了一种处理延迟初始化的集合或代理的便捷方法。只要其Session仍处于打开状态,Hibernate.initialize(cat)将强制初始化代理cat。Hibernate.initialize(cat.getKittens())对于收集小猫具有类似的效果。
有时,需要在关闭会话之前初始化代理或集合。例如,您可以通过调用cat.getSex()或cat.getKittens()。size()来强制初始化。但是,这可能会使代码的读者感到困惑,并且对于通用代码而言并不方便。
静态方法Hibernate.initialize()和Hibernate.isInitialized()为应用程序提供了一种处理延迟初始化的集合或代理的便捷方法。只要其Session仍处于打开状态,Hibernate.initialize(cat)将强制初始化代理cat。Hibernate.initialize(cat.getKittens())对于收集小猫具有类似的效果。
只是简单地获取延迟集合(a.getBlist())不会使其加载- 我最初犯了这个错误。如果我尝试从该集合中获取一些数据(获取项目,获取集合大小),它将加载。调用Hibernate.initialize(..)该集合将执行相同的操作。
a.getBlist()
Hibernate.initialize(..)
因此,遍历实体关联及其各自的关联等,并Hibernate.initialize()在会话内明确初始化它们(例如使用),将在会话关闭后将所有内容加载到会话外可用。
Hibernate.initialize()
使用该方法根本不使用条件提取模式(为什么它们不能按记录工作是另一个问题)。
这是N + 1问题的明显情况,但我可以接受。