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

Mor*_*sen 4 java hibernate jpa hql

我有以下课程:

@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<Bar> bars = new ArrayList<Bar>();

    // ...
}

@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 {
    // ...
}
Run Code Online (Sandbox Code Playgroud)

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

SELECT b FROM Base b LEFT JOIN FETCH b.bars
Run Code Online (Sandbox Code Playgroud)

虽然这有效,但似乎为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 ...
Run Code Online (Sandbox Code Playgroud)

是否有可能告诉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]
Run Code Online (Sandbox Code Playgroud)

我也尝试过使用IN(b.bars) bars,虽然这允许我引用子集合,但它似乎并没有急切地加载我目标的条形集合.

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

Sas*_*shi 5

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

SELECT b FROM Base b LEFT JOIN FETCH b.bars bar LEFT JOIN FETCH bar.baz
Run Code Online (Sandbox Code Playgroud)

这应该只导致一个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;

    //...
}
Run Code Online (Sandbox Code Playgroud)