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

今、私は基本的にすべてをロードしたいのです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 ...

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、を使用してみましたが、これにより子コレクションを参照できますが、私の目標であるbarsコレクションを熱心にロードしていないようです。

私はそれを理解できないように見えるので、なぜこれが起こるのかについての説明もいいでしょう。

4

3 に答える 3

5

(n + 1)を選択せず​​にバーとバズを取得したい場合は、次のhqlを使用してください。

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

これにより、SQLが1つだけになります。

また、「Baz」をフェッチしたくない場合は、Bar->Baz「lazy」から関連付けを行うだけです。

JPAは、デフォルトで、「@OneToOne」および「@ManyToOne」の関連付けに対して「熱心な」フェッチを強制します。したがって、以下に示すように、明示的に怠惰にする必要があります。

@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に怠惰な猫のコレクションがあり、現在10人がセッションにロードされている場合、すべてのPersonを反復処理すると、getCats()の呼び出しごとに1つずつ、10個のSELECTが生成されます。Personのマッピングでcatsコレクションのバッチフェッチを有効にすると、Hibernateはコレクションをプリフェッチできます。

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

バッチサイズが3の場合、Hibernateは4つのSELECTに3、3、3、1のコレクションをロードします。この場合も、属性の値は、特定のセッションで初期化されていないコレクションの予想数によって異なります。

私はそれを使用しています、そしてそれはうまくいきます。ページングを行っていて、常に20レコードのみを選択する場合、batch-size="20"が適切に機能します。20を超える必要がある場合でも、DBへの呼び出しは減少します

于 2012-11-14T13:10:35.273 に答える
0

私のアプローチ(私は限られた、安定した数の第2レベルのエンティティを持っていました)

まず、すべてのバーをセッションに参加させます。

 SELECT bar FROM Bar bar

その後、すべてのBarエンティティがキャッシュに入れられ、クエリは追加のエンティティなしでそれらにアクセスできるようになります。

于 2012-11-14T12:20:19.157 に答える