Hibernate(JPA)如何进行急切查询,加载所有子对象

20

关于我的之前的问题,我想确保所有子对象都被加载,因为我有多个线程可能需要访问数据(从而避免懒加载异常)。我知道做到这一点的方法是在查询(EJB QL)中使用“fetch”关键字。像这样:

select distinct o from Order o left join fetch o.orderLines

假设有一个包含一组OrderLinesOrder类的模型。

我的问题是,如果不使用“distinct”关键字,似乎会返回每个OrderLineOrder。我这样做对吗?

更重要的是,是否有一种方法可以拉取所有子对象,无论多么深入?我们大约有10-15个类,对于服务器,我们需要加载所有内容...我避免使用FetchType.EAGER,因为这意味着它总是急切的,特别是Web前端加载所有内容 - 但也许这是正确的方式 - 这是你所做的吗?我记得我们以前尝试过这个,然后得到非常慢的网页 - 但也许这意味着我们应该使用第二级缓存?

7个回答

16

改变注释是一个不好的想法,在运行时无法将其更改为lazy。最好让所有内容都变成lazy,并根据需要获取。

如果没有映射,我不确定我是否理解您的问题。 对于您描述的用例,左连接获取应该是您所需的全部内容。 当然,如果orderline有一个order作为其父级,则每个orderline都会返回一个order。


最好把所有东西都变成懒加载,需要时再获取。 - 我完全同意。 - Cavaleiro

8

7
这个人需要在运行时改变策略。 - Imaky

3

您尝试过使用结果转换器吗?如果使用 Criteria 查询,您可以应用结果转换器(尽管 在分页和结果转换器方面存在一些问题):

Criteria c = ((Session)em.getDelegate()).createCriteria(Order.class);
c.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
c.list();
em.getDelegate()只在使用hibernate时有效,它是一种hack方法。
更重要的是,是否有一种方法可以获取所有子对象,无论深度如何?我们大约有10-15个类,对于服务器来说,我们需要加载所有内容...我避免使用FetchType.EAGER,因为这意味着它总是渴望的,特别是Web前端会加载所有内容-但也许这就是最好的方法-这是你做的吗?我似乎记得我们以前尝试过这样做,然后网页变得非常缓慢-但也许这意味着我们应该使用二级缓存?
如果您仍然感兴趣,我在这个线程how to serialize hibernate collections中回答了类似的问题。
基本上,您使用一个称为dozer的实用程序将bean映射到另一个bean,通过这样做,您触发了所有懒加载。可以想象,如果所有集合都被急切地获取,这将起作用得更好。

2
您可能可以使用(分离的)Criteria查询并设置抓取模式来完成此类操作。例如:
Session s = ((HibernateEntityManager) em).getSession().getSessionFactory().openSession();
DetachedCriteria dc = DetachedCriteria.forClass(MyEntity.class).add(Expression.idEq(id));
dc.setFetchMode("innerTable", FetchMode.JOIN);
Criteria c = dc.getExecutableCriteria(s);
MyEntity a = (MyEntity)c.uniqueResult();

谢谢,但问题在于我想在主EM所有线程之外使用持久化对象,以获取有关当前状态的瞬态数据。因此,我不能使用主EM,但也希望使用相同的对象 :( - Chris Kimpton

0

这仅适用于ManyToOne关系,对于它们来说@ManyToOne(fetch=FetchType.EAGER)可能是合适的。

强烈不建议或不支持急切地获取多个OneToMany关系,正如您可以在Jeremy发布的链接中阅读到的那样。想想需要执行这样的获取所需的SQL语句...


0
我所做的是重构代码,以保持对象到实体管理器的映射,并在每次需要刷新时关闭旧的对象实体管理器并打开一个新的实体管理器。我使用了上面的查询,没有使用fetch,因为这对我的需求来说太深入了——只是进行普通的连接就可以拉取OrderLines了,而fetch会使它变得更加深入。
我只需要对大约20个对象进行此操作,因此我认为拥有20个打开的实体管理器的资源开销不是问题——尽管当这个项目上线时,数据库管理员可能会有不同的看法...
我还重新设计了一些东西,使得数据库工作在主线程上,并具有实体管理器。
Chris

-4
如果问题只是LazyInitializationExceptions,您可以通过添加OpenSessionInViewFilter来避免该问题。这将允许在视图中加载对象,但不会解决速度问题。
     <filter>
        <filter-name>hibernateFilter</filter-name>
        <filter-class> org.springframework.orm.hibernate3.support.OpenSessionInViewFilter
        </filter-class>
    </filter>
    <filter-mapping>
        <filter-name>hibernateFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

谢谢,但是这个答案涉及到Web应用程序/Servlet,而不是独立的应用程序,而我正在处理的就是独立的应用程序。 - Chris Kimpton

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接