我正在获取一长串实体,这些实体引用其他引用的实体......最后,通常所有实体都引用单个user
作为owner
。并不奇怪,因为所查询的是属于单个user
的实体。许多行中有更多的部分重复;实际上,只有一小部分是唯一数据。由于查询似乎很慢,我可以通过使用
criteria.setFetchMode(path, FetchMode.SELECT);
这适用于我的上述情况,但是当查询许多用户(作为管理员)时,它变得非常糟糕,因为hibernate为每个 user
发出单独的查询,而不是像
SELECT * FROM User WHERE id IN (?, ?, ..., ?)
或根本不提取它们(每个实体不能比一个查询更糟糕)。我想知道我错过了什么?
因此,我没有获取大量冗余数据,而是遇到了1 + N问题,显然会有1 + 1个查询。
fetch=FetchType.LAZY
放在字段上;懒惰应该是查询特定的?)我认为这不重要,但我的课程就像
class Child {
@ManyToOne Father father;
@ManyToOne Mother mother;
...
}
class Father {
@ManyToOne User owner;
...
}
class Mother {
@ManyToOne User owner;
...
}
,查询就像
createCriteria(Child.class)
.add(Restrictions.in("id", idList))
.add(Restrictions.eq("isDeleted", false))
.createAlias("Father", "f")
.add(Restrictions.eq("f.isDeleted", false))
.setFetchMode("f.owner", FetchMode.SELECT)
.createAlias("Mother", "m")
.add(Restrictions.eq("m.isDeleted", false))
.setFetchMode("m.owner", FetchMode.SELECT)
.list();
重要的是owner
没有被使用并且可以被代理。 FetchMode.SELECT
的javadoc说
使用单独的选择
急切地获取
所以它基本上承诺我想要的1 + 1查询,而不是“每个实体使用单独的选择”。
答案 0 :(得分:3)
Fetch profiles旨在帮助您实现您想要的目标,但目前非常有限,您只能使用连接样式的获取配置文件覆盖默认的获取计划/策略(您可以做一个懒惰协会渴望,但反之亦然)。但是,您可以使用此trick来反转该行为:默认情况下使关联变为惰性,并默认为所有会话/事务启用配置文件。然后禁用您希望延迟加载的交易中的个人资料。
恕我直言,上面的解决方案看起来过于繁琐,我在大多数用例中使用的方法,以避免加载冗余数据和N + 1选择问题是使关联变得懒惰并定义batch size。
答案 1 :(得分:1)
我写了一个小项目来证明这种行为。根据您的条件生成的SQL如下:
select
this_.id as id1_0_4_,
this_.father_id as father_i3_0_4_,
this_.isDeleted as isDelete2_0_4_,
this_.mother_id as mother_i4_0_4_,
f1_.id as id1_1_0_,
f1_.isDeleted as isDelete2_1_0_,
f1_.owner_id as owner_id3_1_0_,
user5_.id as id1_3_1_,
user5_.isDeleted as isDelete2_3_1_,
m2_.id as id1_2_2_,
m2_.isDeleted as isDelete2_2_2_,
m2_.owner_id as owner_id3_2_2_,
user7_.id as id1_3_3_,
user7_.isDeleted as isDelete2_3_3_
from
Child this_
inner join
Father f1_
on this_.father_id=f1_.id
left outer join
User user5_
on f1_.owner_id=user5_.id
inner join
Mother m2_
on this_.mother_id=m2_.id
left outer join
User user7_
on m2_.owner_id=user7_.id
where
this_.id in (
?, ?
)
and this_.isDeleted=?
and f1_.isDeleted=?
and m2_.isDeleted=?
如其他答案中所述,如果实体关系设置为EAGER
,即JPA默认值,则您无法更改Criteria API中的提取模式。需要将获取模式更改为LAZY
。
你可以看到它here
答案 2 :(得分:1)
总结一下我的挫败感......在这方面,Hibernate充满了惊喜(错误?):
@ManyToOne(fetch=FetchType.LAZY)
声明属性,否则无法更改任何内容FetchType.EAGER
,这是愚蠢的,因为无法覆盖criteria.setFetchMode(path, FetchMode.SELECT)
是毫无意义的,因为它总是一个无操作(要么被忽略,因为属性不可覆盖,或者属性已经很懒)!@BatchSize
注释@BatchSize
注释会被默默忽略 为了获得我想要的东西(两个SQL查询),我只需要两件事:
@ManyToOne(fetch=FetchType.LAZY)
@BatchSize(size=aLot)
放置在属性类这很简单,但有点难以找到(因为上面所有被忽略的事情)。我还没有查看过fetch配置文件。
答案 3 :(得分:1)
除非声明属性
@ManyToOne(fetch=FetchType.LAZY)
,你无法改变任何事情
是的,至少在目前情况下,直到获取配置文件功能被扩展为提供将预先加载更改为延迟的能力。
默认为
FetchType.EAGER
,这是愚蠢的,因为它不可能 覆盖
是的,我同意它很糟糕,但在Hibernate本机API中,默认情况下一切都是懒惰的;除非另有明确规定,否则JPA要求一个协会渴望。
使用criteria.setFetchMode(path,FetchMode.SELECT)毫无意义 它总是一个无操作(要么被忽略,因为它 不可覆盖的财产或财产的热情是懒惰的 已)!
有了它,你应该能够覆盖其他懒惰的提取模式。请参阅主要Hibernate贡献者之一关于javadoc混淆的HHH-980和this comment。
默认情况下,懒惰地获取1 + N问题
它与延迟加载无关,如果你没有在同一个查询中获取急切加载的关联,它也是预先加载的默认值。
可以通过类级
来控制它@BatchSize
注释
你必须将它放在类级别才能使它与该实体的一个关联生效; this answer很有帮助。对于集合关联(与其他实体中定义的实体的多对多关联),您可以灵活地为每个关联单独定义它。