强制hibernate在不改变映射的情况下加载多个关联 [英] Force hibernate to eagerly load multiple associations without changing mapping

查看:135
本文介绍了强制hibernate在不改变映射的情况下加载多个关联的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

  @Entity $ b $我有一些实体具有懒惰的一对多关系b 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)
私人字符串ID;
}

@实体
类别B {
}

@实体
类别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,可为空= false)
列表< F> flist = new ArrayList<>();
}

@实体
类E {
}

@实体
类F {
}
$ / pre>

在某些情况下(非常罕见),我希望加载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(); //第152行 - 调试
a.getClist()。size(); //第153行 - 调试

当我尝试访问内容时,我在第152行

  org.hibernate.LazyInitializationException:
未能延迟初始化角色集合:
A.blist,无法初始化代理 - 无会话

如果我在条件中的所有地方都将获取策略更改为 JOIN ,我得到相同的异常,但是在第153行(其他字,第一个关联被加载,但不是其他)。



编辑:阿列克谢Malev建议提取模式设置为别名;它似乎确实如此。使用以下标准:

 标准c =会话
.createCriteria(A.class)
.add(Restrictions.eq(id,someValue))
.setFetchMode(blist,JOIN)
.setFetchMode(clist,JOIN);

我收到了一个不同的异常: org.hibernate.loader.MultipleBagFetchException:无法同时在149行上获取多个行囊 。因此,连接读取不是一个选项。



问题是,我如何加载整个事物?



Hibernate版本4.2.12.Final

通过使用稍微不同的方法,我找到了解决我原来的问题的方法。
引用Hibernate ORM文档:


有时代理或集合需要在关闭会话之前初始化。例如,可以通过调用cat.getSex()或cat.getKittens()。size()来强制初始化。但是,这可能会让代码读者感到困惑,并且对于泛型代码来说并不方便。

静态方法Hibernate.initialize()和Hibernate.isInitialized(),为应用程序提供使用懒惰初始化集合或代理的便捷方式。只要Session的Session仍然打开,Hibernate.initialize(cat)会强制代理cat的初始化。 Hibernate.initialize(cat.getKittens())对于小猫集合有类似的效果。

只需获取lazy collection( a.getBlist())不会使它加载 - 我最初犯了这个错误。如果我尝试从该集合中获取一些数据(获取一个项目,获取集合大小),它将加载。在该集合上调用 Hibernate.initialize(..)也可以做到这一点。

因此,迭代实体关联,以及它们各自的关联等等,并且在会话中明确地初始化它们(例如使用 Hibernate.initialize())将会加载会话之外的所有东西,一旦它被关闭。

标准提取模式根本不使用这种方法(为什么它们不能像记录的那样工作是另一个问题)。

这是一个明显的N + 1问题,但我可以忍受。


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.

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:

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).

EDIT: Alexey Malev suggested that fetch mode is set for an alias; it does seem to be true. With a following criteria:

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.

The question is, how do I load the whole thing?

Hibernate version 4.2.12.Final

解决方案

I found a solution to my original problem by using a slightly different approach. Quoting Hibernate ORM documentation:

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.

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.

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.

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.

这篇关于强制hibernate在不改变映射的情况下加载多个关联的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆