避免n + 1渴望获取子集合元素关联

我有以下课程:

@Entity @Table(name = "base") @Inheritance(strategy = InheritanceType.SINGLE_TABLE) @DiscriminatorColumn(name = "DISCRIMINATOR", discriminatorType = DiscriminatorType.STRING) @ForceDiscriminator public class Base { // ... } @Entity @DiscriminatorValue("foo") public class Foo extends Base { @OneToMany( mappedBy = "foo", cascade=CascadeType.ALL ) private List bars = new ArrayList(); // ... } @Entity public class Bar { @ManyToOne (optional = false) @JoinColumn(name = "foo_id" ) private Foo foo; @OneToOne @JoinColumn(name = "baz_id", nullable = false) private Baz baz; //... } @Entity public class Baz { // ... } 

现在我基本上想要加载所有Base ,但是在适用时需要加载吧,所以我使用以下查询:

 SELECT b FROM Base b LEFT JOIN FETCH b.bars 

虽然这有效,但似乎为Bar实体生成了SELECT N + 1问题:

 Hibernate: /* SELECT b FROM Base b LEFT JOIN FETCH b.bars */ SELECT ... Hibernate: /* load com.company.domain.Baz */ SELECT ... Hibernate: /* load com.company.domain.Baz */ SELECT ... 

是否有可能告诉hibernate急切地为子集合中的每个元素加载一个关联而不诉诸N + 1 SELECT?

我尝试了以下查询的内容,这显然不起作用,因为它的集合:

 SELECT b FROM Base b LEFT JOIN FETCH b.bars LEFT JOIN FETCH b.bars.baz //Results in: illegal attempt to dereference collection [Foo.id.bars] with element property reference [baz] 

我也尝试使用IN(b.bars) bars ,虽然这允许我引用IN(b.bars) bars ,但它似乎并不急于加载我的目标吧集合。

解释为什么会发生这种情况也很好,因为我似乎无法弄明白。

如果你想用out(n + 1)选择检索Bar和Baz,请使用以下hql。

 SELECT b FROM Base b LEFT JOIN FETCH b.bars bar LEFT JOIN FETCH bar.baz 

这应该只导致一个sql。

此外,如果你不想获取’Baz’,只需从Bar-> Baz’懒惰’进行关联。

默认情况下,JPA强制’eager’获取’@OneToOne’和’@ManyToOne’关联。 所以,你必须明确地使它变得懒惰,如下所示。

 @Entity public class Bar { @OneToOne @JoinColumn(name = "baz_id", nullable = false, fetch=FetchType.Lazy) private Baz baz; //... } 

我的方法(我有一个有限的,稳定数量的二级实体)

首先,让所有Bars进入会话:

  SELECT bar FROM Bar bar 

之后,所有Bar实体都将位于缓存中,您的查询将无需其他实体即可访问它们。

我想说在这种情况下,获取策略的改变可能会有所帮助。 文件说:

http://docs.jboss.org/hibernate/orm/4.1/manual/en-US/html_single/#performance-fetching-batch

提取:

您还可以启用批量提取集合。 例如,如果每个Person都有一个懒惰的Cats集合,并且当前在Session中加载了10个人,则遍历所有人将生成10个SELECT,每次调用getCats()一个。 如果在Person的映射中为cats集合启用批量提取,Hibernate可以预取集合:

   ...   

批量大小为3时,Hibernate将在四个SELECT中加载3个,3个,3个,1个集合。 同样,属性的值取决于特定会话中未初始化集合的预期数量。

我正在使用它并且效果很好。 如果我正在分页并且总是只选择20条记录,则批量大小=“20”的效果很好。 如果我需要20以上,仍然会减少对DB的调用