Java 强制休眠在不改变映射的情况下急切地加载多个关联
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/23676286/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Force hibernate to eagerly load multiple associations without changing mapping
提问by enlait
I have a few entities with lazy one to many relationships (logic omitted for brevity):
我有一些具有惰性一对多关系的实体(为简洁起见省略了逻辑):
@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{
}
In some (very rare) case I want to load an instance of A and all of its associations eagerly. The reason for that is that I want to make some modifications on that A instance and it's children as a whole, and then either save or discard them, depending on user input.
在某些(非常罕见的)情况下,我想急切地加载 A 的实例及其所有关联。这样做的原因是我想对该 A 实例及其整个子项进行一些修改,然后根据用户输入保存或丢弃它们。
If I were to load things as they are needed, I'd have to reattach entities to a new session on user request, but they are being modified, and modifications should not be persisted yet.
如果我要根据需要加载东西,我必须根据用户请求将实体重新附加到新会话,但它们正在被修改,并且修改不应该被持久化。
So I write something like that:
所以我写了这样的东西:
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
When I try to access stuff I get an exception on line 152
:
当我尝试访问内容时,出现以下异常line 152
:
org.hibernate.LazyInitializationException:
failed to lazily initialize a collection of role:
A.blist, could not initialize proxy - no Session
If I change fetch strategy to JOIN
everywhere in the criteria, I get the same exception, but on line 153
(in other words, the first association gets loaded, but not the others).
如果我将 fetch 策略更改JOIN
为标准中的所有位置,我会得到相同的异常,但在line 153
(换句话说,第一个关联被加载,但不是其他关联)。
EDIT: Alexey Malev suggested that fetch mode is set for an alias; it does seem to be true. With a following criteria:
编辑:Alexey Malev 建议为别名设置获取模式;这似乎是真的。符合以下标准:
Criteria c = session
.createCriteria(A.class)
.add(Restrictions.eq("id", someValue))
.setFetchMode("blist", JOIN)
.setFetchMode("clist", JOIN);
I'm getting a different Exception:org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bags
on line 149. So, join-fetching is not an option.
我得到了一个不同的异常:org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bags
在第 149 行。所以,join-fetching 不是一个选项。
The question is, how do I load the whole thing?
问题是,我如何加载整个东西?
Hibernate version 4.2.12.Final
休眠版本 4.2.12.Final
采纳答案by enlait
I found a solution to my original problem by using a slightly different approach. Quoting Hibernate ORM documentation:
我通过使用稍微不同的方法找到了原始问题的解决方案。引用 Hibernate ORM 文档:
Sometimes a proxy or collection needs to be initialized before closing the Session. You can force initialization by calling cat.getSex() or cat.getKittens().size(), for example. However, this can be confusing to readers of the code and it is not convenient for generic code.
The static methods Hibernate.initialize() and Hibernate.isInitialized(), provide the application with a convenient way of working with lazily initialized collections or proxies. Hibernate.initialize(cat) will force the initialization of a proxy, cat, as long as its Session is still open. Hibernate.initialize( cat.getKittens() ) has a similar effect for the collection of kittens.
有时需要在关闭会话之前初始化代理或集合。例如,您可以通过调用 cat.getSex() 或 cat.getKittens().size() 来强制初始化。但是,这可能会使代码的读者感到困惑,并且对于通用代码来说并不方便。
静态方法 Hibernate.initialize() 和 Hibernate.isInitialized() 为应用程序提供了一种方便的方式来处理延迟初始化的集合或代理。Hibernate.initialize(cat) 将强制初始化代理 cat,只要它的 Session 仍然打开。Hibernate.initialize( cat.getKittens() ) 对小猫的集合也有类似的效果。
Simply getting lazy collection (a.getBlist()
) does not make it load - I initially made that mistake. If I try to get some data from that collection (get an item, get collection size) it will load. Calling Hibernate.initialize(..)
on that collection will do the same.
简单地获取惰性集合 ( a.getBlist()
) 并不会使其加载 - 我最初犯了这个错误。如果我尝试从该集合中获取一些数据(获取一个项目,获取集合大小),它将加载。调用Hibernate.initialize(..)
该集合也会做同样的事情。
So, iterating over entity associations, and their respective associations, etc, and explicitly initializing them (eg with Hibernate.initialize()
) within session will load everything to be available outside the session once it's closed.
因此,迭代实体关联及其各自的关联等,并Hibernate.initialize()
在会话内显式初始化它们(例如,使用)将在会话关闭后加载所有可用的内容。
Criteria fetch modes are not used at all with that approach (why won't they work as documented is another question).
该方法根本不使用标准获取模式(为什么它们不能按文档工作是另一个问题)。
It is an obvious case of N+1 problem, but something I can live with.
这是 N+1 问题的一个明显案例,但我可以接受。
回答by Alexey Malev
I think you're using wrong fetch mode. Most likely you need JOIN
.
Try this instead:
我认为您使用了错误的获取模式。您很可能需要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
Note - I have added fetch mode for alias too. The behavior when you're not able to load any list which has an alias leads to that guess..
注意 - 我也为别名添加了获取模式。当您无法加载任何具有别名的列表时的行为会导致该猜测..
回答by MonoThreaded
For the record I had a similar problem due to the key value setFecthMode(key,...)
I was using table names instead of field names.
对于记录,由于setFecthMode(key,...)
我使用的是表名而不是字段名的键值,我遇到了类似的问题。