Java 返回多个部分重复的联接的高效Hibernate条件
我正在获取一个长长的实体列表,这些实体引用了其他实体,这些实体引用了。。。最后,他们通常将一个Java 返回多个部分重复的联接的高效Hibernate条件,java,hibernate,optimization,criteria,hibernate-criteria,Java,Hibernate,Optimization,Criteria,Hibernate Criteria,我正在获取一个长长的实体列表,这些实体引用了其他实体,这些实体引用了。。。最后,他们通常将一个用户称为他们的所有者。这并不奇怪,因为查询的是属于单个用户的实体。多行复制的零件较多;实际上,只有一小部分是唯一的数据。由于查询速度似乎很慢,我想通过使用 criteria.setFetchMode(path, FetchMode.SELECT); 在我上面的例子中,这是可行的,但是当查询多个用户(作为管理员)时,情况会变得很糟糕,因为hibernate会为每个用户发出一个单独的查询,而不是像这样的查
用户
称为他们的所有者
。这并不奇怪,因为查询的是属于单个用户的实体。多行复制的零件较多;实际上,只有一小部分是唯一的数据。由于查询速度似乎很慢,我想通过使用
criteria.setFetchMode(path, FetchMode.SELECT);
在我上面的例子中,这是可行的,但是当查询多个用户(作为管理员)时,情况会变得很糟糕,因为hibernate会为每个用户发出一个单独的查询,而不是像这样的查询
SELECT * FROM User WHERE id IN (?, ?, ..., ?)
或者根本就不获取它们(每个实体只能得到一个查询)。我想知道我错过了什么
因此,我没有获取大量冗余数据,而是遇到了1+N问题,显然1+1查询就可以了
- 是否有方法指示Hibernate使用正确的查询
- 是否有一种方法可以通过在条件本身中指定来防止Hibernate获取所有者(而不是将
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的javadoc。选择
使用一个单独的选择按钮,急切地获取
因此,它基本上承诺了我想要的1+1查询,而不是“对每个实体使用单独的select”。我写了一个小项目来演示这种行为。根据您的条件生成的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=?
更改criteria API中的FetchMode不会影响查询。所有者数据仍在查询中李>
Id位于“in”子句中,Hibernate没有对每个Id发出单独的查询
如其他答案中所述,如果实体关系设置为EAGER
,即JPA默认值,则无法更改Criteria API中的获取模式。提取模式需要更改为惰性
您可以看到,旨在帮助您实现您想要的目标,但目前非常有限,您只能使用联接样式的获取配置文件覆盖默认的获取计划/策略(您可以使惰性关联变得急切,但反之亦然)。但是,您可以使用它来反转该行为:默认情况下使关联变为惰性,并默认情况下为所有会话/事务启用概要文件。然后在事务中禁用要延迟加载的配置文件
上面的解决方案看起来太麻烦了,我在大多数用例中使用的避免加载冗余数据和N+1选择问题的方法是使关联变懒并定义 总结一下我的挫败感。。。Hibernate在这方面充满了惊喜(bug?)
- 除非使用
@ManyToOne(fetch=FetchType.LAZY)
声明属性,否则无法更改任何内容
- 默认值是
FetchType.EAGER
,这很愚蠢,因为它不能被重写
- 使用
标准。setFetchMode(path,FetchMode.SELECT)
是毫无意义的,因为它总是一个no-op(要么由于属性的不可重写的渴望而被忽略,要么属性已经懒惰)李>
- 默认情况下,延迟抓取会导致1+N问题
- 它可以通过类级别的
@BatchSize
注释进行控制
- 在标量字段上放置
@BatchSize
注释会被静默忽略
为了得到我想要的(两个SQL查询),我只需要两件事:
- 使用
@ManyToOne(fetch=FetchType.LAZY)
- 将
@BatchSize(size=aLot)
放在属性的类上
这很简单,但有点难找到(因为上面所有被忽略的事情)。我还没有查看获取配置文件
除非该财产是以
@ManyToOne(fetch=FetchType.LAZY)
,您不能更改任何内容
至少目前是这样,直到fetch配置文件功能被扩展以提供将急切加载更改为延迟加载的能力
默认值为FetchType.EAGER
,这是愚蠢的,因为它不可能
凌驾
是的,我同意这是不好的,但在Hibernate原生API中,默认情况下一切都是懒惰的;除非另有明确规定,否则JPA要求一个协会表现出渴望
使用criteria.setFetchMode(路径,FetchMode.SELECT)与
它总是一个无操作(或者因为
属性的不可重写的渴望或属性是懒惰的
已经)!
使用它,您应该能够覆盖其他延迟获取模式。请参阅Hibernate主要贡献者之一关于javadoc混淆的介绍
默认情况下,延迟抓取会导致1+N问题
具体来说,它与延迟加载无关,如果不在同一查询中获取急切加载的关联,它也是急切加载的默认设置
它可以通过类级别的@BatchSize
注释进行控制
您必须将其放在类级别,才能使其在与该实体的一个关联上生效;这很有帮助。对于集合关联(与其他实体中定义的实体的多个关联),您可以灵活地为每个关联分别定义它。N+1问题?你试过FetchMode.JOIN或FetchMode.SUBSELECT吗?@dimitrisli当然,如果是2,则改为N+1FetchMode.JOIN
是我想要避免的,因为它读取大量重复数据SUBSELECT
存在于org.hibernate.annotations.FetchMode
中,但不存在于我正在使用的org.hibernate.FetchMode
中(I