4

我有以下课程:

@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 {
    // ...
}

现在我基本上想加载 all 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 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,虽然这允许我引用子集合,但它似乎并没有急切地加载 bar 集合,这是我的目标。

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

4

3 回答 3

5

如果您想在没有 (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 'lazy' 建立关联。

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

@Entity
public class Bar {

    @OneToOne
    @JoinColumn(name = "baz_id", nullable = false, fetch=FetchType.Lazy)
    private Baz baz;

    //...
}
于 2012-11-15T13:50:30.777 回答
0

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

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

提炼:

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

<class name="Person">
    <set name="cats" batch-size="3">
        ...
    </set>
</class>

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

我正在使用它并且效果很好。如果我在分页并且总是只选择 20 条记录,那么 batch-size="20" 效果很好。如果我需要超过 20 个,对 DB 的调用仍然会减少

于 2012-11-14T13:10:35.273 回答
0

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

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

 SELECT bar FROM Bar bar

之后,所有 Bar 实体都将在缓存中,您的查询将能够在没有其他实体的情况下访问它们。

于 2012-11-14T12:20:19.157 回答