我们正在尝试实现一个简单的分层数据模型,如下所示。
public class Team {
/* ... */
@OneToMany(fetch = FetchType.LAZY, mappedBy = "team")
protected List<Member> members;
}
public class Member {
/* ... */
@ManyToOne(fetch = FetchType.LAZY)
protected Team team;
@OneToMany(fetch = FetchType.LAZY, mappedBy = "member")
protected List<Assignment> assignments;
}
public class Assigment {
/* ... */
@ManyToOne(fetch = FetchType.LAZY)
protected Member member;
}
映射背后的思想非常简单——一个团队由成员组成,每个成员依次分配一些职责。
问题:创建一个查询,该查询将返回团队->成员->分配的层次结构,并对分配施加条件(例如,截止日期在<1周)。
目前的解决方案:
- 查询所有Team实体并在事务中的两级集合上迭代,过滤掉那些不在时间范围内的分配。优点:简单。缺点:可能开销大。 查询后
:
SELECT t FROM Team t left join fetch t.members m left join fetch m.assignments
不幸的是,即使没有赋值过滤的任何条件,查询也会失败,抛出:
org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bags
我不明白为什么它不允许在两个关系上同时抓取,因为Hibernate文档中显示了一个类似的例子:
from Cat as cat
inner join fetch cat.mate
left join fetch cat.kittens child
left join fetch child.kittens
问题:
- 是否有可能用单个SQL语句获取过滤叶级(分配)的整个树状集合?如果不是,建议的解决方案中有什么可以改进的?
- 为什么双取不能正常工作?
附加信息:我们使用Hibernate 4.3.5。最后,PostgreSQL .
是否可以使用filtered获取整个树状集合叶级(赋值)与单个SQL语句?如果不是,那又是什么?可以在提议的解决方案中改进吗?
是的,有可能。但是,您需要将映射更改为使用Set
而不是List
。
为什么双取不能正常工作?
当你试图获取多个集合时,你不能在同一个查询中有两个或两个以上的List
。
因此,将赋值或成员从List
更改为Set
。
要过滤分配,不能在提取的子句中这样做。如果您想获取所有集合并在赋值中进行筛选,您的查询可以是:
SELECT t FROM Team t
join fetch t.members m -- only for fetch
join fetch m.assignments -- only for fetch
join t.members member -- new join to do the where
join member.assigments assigment -- new join to do the where
where assigment.anyAttribute = someValue -- filter assigment
但是,正如@Vlad所说,这种解决方案的笛卡尔积可能是巨大的,并且在SQL结果中带来很多不必要的信息。
双取意味着笛卡尔积,所以如果你有100个团队,100个成员和100个任务,你将得到100 x 100 x 100 = 1,000,000个结果。
最好的获取方式是自底向上。
SELECT a FROM Assigment a
inner join fetch a.member m
inner join fetch m.team t
,自底向上重建树。您可以在Java中使用EntityGraphBuilder
来映射它。