我正在尝试使用Hibernate从表中检索大约1亿行。我有一个持久化的实体项目,其中包含一个费用集合(另一个持久化实体)。鉴于我将迭代结果并访问每个对象的费用,我想急切地获取费用以避免n + 1问题。
我还应该提到我想将它加入另一个名为Provider的表(一对一映射但没有外键)。我试过了:
String query = "select new " + Order.class.getName()
+ "(i, p) from Item i left join fetch i.fees f, Provider p where "
+ "p.factoryId=i.factoryId and p.factoryRef=i.factoryRef";
return session.createQuery(query).scroll();
我的Order类包含Provider字段和Item字段。我收到这个错误:
引起:org.hibernate.QueryException:查询指定的连接 获取,但获取的关联的所有者不存在 选择列表
我想最终得到一个可滚动的Order列表,其中包含Item(收取费用)和Provider。
答案 0 :(得分:1)
来自SelectClause
的代码会给您带来麻烦:
if ( !fromElementsForLoad.contains( origin ) ) {
throw new QueryException(
"query specified join fetching, but the owner " +
"of the fetched association was not present in the select list " +
"[" + fromElement.getDisplayText() + "]"
);
正如您所看到的,当提到fetch关键字时,hibernate会检查您是否要求提取装饰字段父级。 fromElementsForLoad.contains( origin )
他们可能会这样做是为了保护您免于进行冗余连接,这会使您在性能上花费很多。这是一件好事,因为如果你从不使用它,就没有理由获取关联。
我相信在您的情况下 - 将Item.class
包装在新Order.class
中会隐藏您在查询中使用获取装饰字段父级的事实。
我目前没有调试功能,所以我无法验证这一点。尝试从SelectClause.class调试这个确切的行,看看fromElementsForLoad
集合中包含哪些元素。
如果你想避免n + 1问题,我建议在查询后初始化Order.class。您只能选择项目和提供商。
如果你无法验证这一点,我下周会找一台合适的电脑并扩展我的答案。
答案 1 :(得分:0)
尝试删除left join fetch i.fees f
并在映射中执行急切提取。