web-dev-qa-db-ja.com

JPAの熱心なフェッチは参加しません

JPAのフェッチ戦略は正確に何を制御しますか?私は熱心と怠zyの違いを検出できません。どちらの場合も、JPA/Hibernateは多対1の関係を自動的に結合しません。

例:個人の住所は1つです。アドレスは多くの人に属することができます。 JPAアノテーション付きエンティティクラスは次のようになります。

@Entity
public class Person {
    @Id
    public Integer id;

    public String name;

    @ManyToOne(fetch=FetchType.LAZY or EAGER)
    public Address address;
}

@Entity
public class Address {
    @Id
    public Integer id;

    public String name;
}

JPAクエリを使用する場合:

select p from Person p where ...

JPA/HibernateはPersonテーブルから選択する1つのSQLクエリを生成し、次にeachpersonの個別のアドレスクエリを生成します。

select ... from Person where ...
select ... from Address where id=1
select ... from Address where id=2
select ... from Address where id=3

これは大きな結果セットには非常に悪いです。 1000人の人がいる場合、1001個のクエリ(人から1人、住所から1000人)が生成されます。これは、MySQLのクエリログを見ているためです。アドレスのフェッチタイプをeagerに設定すると、JPA/Hibernateが自動的に結合でクエリを実行することを理解していました。ただし、フェッチタイプに関係なく、リレーションシップに対して個別のクエリが生成されます。

明示的に参加するように指示した場合にのみ、実際に参加します。

select p, a from Person p left join p.address a where ...

ここに何かが足りませんか?多対1のリレーションシップを結合するように、すべてのクエリを手作業でコーディングする必要があります。 MySQLでHibernateのJPA実装を使用しています。

Edit:表示されます(Hibernate FAQ here および here)を参照FetchTypeはJPAクエリに影響しないので、私の場合は、明示的に参加するように指示しています。

103
Steve Kuo

JPAは、フェッチ戦略を選択するためのアノテーションのマッピングに関する仕様を提供していません。一般に、関連するエンティティは、次のいずれかの方法で取得できます。

  • SELECT =>ルートエンティティに対して1つのクエリ+各ルートエンティティの関連マッピングエンティティ/コレクションに対して1つのクエリ=(n + 1)クエリ
  • SUBSELECT =>ルートエンティティの1つのクエリ+関連するマッピングされたエンティティの2番目のクエリ/最初のクエリで取得されたすべてのルートエンティティのコレクション= 2つのクエリ
  • JOIN =>ルートエンティティとそれらのマッピングされたエンティティ/コレクションの両方を取得する1つのクエリ= 1クエリ

したがって、SELECTJOINは2つの極端であり、SUBSELECTはその中間にあります。ドメインモデルに基づいて適切な戦略を選択できます。

デフォルトでは、SELECTはJPA/EclipseLinkとHibernateの両方で使用されます。これは次を使用してオーバーライドできます。

_@Fetch(FetchMode.JOIN) 
@Fetch(FetchMode.SUBSELECT)
_

hibernateで。 @Fetch(FetchMode.SELECT)を使用して明示的にSELECTモードを設定することもできます。これはバッチサイズを使用して調整できます。 @BatchSize(size=10)

EclipseLinkの対応する注釈は次のとおりです。

_@JoinFetch
@BatchFetch
_
89
Yadu

「mxc」が正しい。 fetchTypeは、whenを指定するだけで、リレーションを解決する必要があります。

外部結合を使用して積極的な読み込みを最適化するには、追加する必要があります

@Fetch(FetchMode.JOIN)

あなたのフィールドに。これは、休止状態固有の注釈です。

43
rudolfson

FetchType属性は、プライマリエンティティが取得されたときに注釈付きフィールドをすぐに取得するかどうかを制御します。 fetch文の構築方法を必ずしも指示するわけではありません。実際のSQL実装は、toplink/hibernateなどを使用しているプロバイダーに依存します。

fetchType=EAGERを設定した場合、これは、注釈付きフィールドにエンティティの他のフィールドと同時に値が入力されることを意味します。したがって、entitymanagerを開いて個人オブジェクトを取得してからentitymanagerを閉じても、その後person.addressを実行しても遅延ロード例外はスローされません。

fetchType=LAZYを設定した場合、フィールドはアクセスされたときにのみ入力されます。それまでにentitymanagerを閉じた場合、person.addressを実行すると遅延ロード例外がスローされます。フィールドをロードするには、em.merge()を使用してエンティティをentitymangersコンテキストに戻し、フィールドにアクセスしてからentitymanagerを閉じる必要があります。

顧客注文のコレクションを持つ顧客クラスを構築するときに、遅延読み込みが必要になる場合があります。顧客リストを取得したいときに顧客のすべての注文を取得した場合、顧客名と連絡先の詳細のみを検索する場合、これは高価なデータベース操作になる可能性があります。後までdbアクセスを残すのが最善です。

質問の2番目の部分-最適化されたSQLを生成するために休止状態にする方法

Hibernateを使用すると、最も効率的なクエリを作成する方法に関するヒントを提供できますが、テーブルの作成に何か問題があると思われます。テーブルで関係が確立されていますか? Hibernateは、特にインデックスなどが欠落している場合、単純なクエリが結合よりも高速になると判断した可能性があります。

36
mxc

で試してください:

select p from Person p left join FETCH p.address a where...

JPA2/EclipseLinkと同様に機能しますが、この機能は JPA1も に存在するようです:

18
sinuhepop

Hibernateの代わりにEclipseLinkを使用する場合、「クエリヒント」によってクエリを最適化できます。 Eclipse Wikiのこの記事を参照してください: EclipseLink/Examples/JPA/QueryOptimization

「Joined Reading」に関する章があります。

参加するには、複数のことを行うことができます(eclipselinkを使用)

  • jpqlでは、左結合フェッチを行うことができます

  • 名前付きクエリでは、クエリヒントを指定できます

  • typedQueryでは、次のように言うことができます

    query.setHint("eclipselink.join-fetch", "e.projects.milestones");

  • バッチフェッチヒントもあります

    query.setHint("eclipselink.batch", "e.address");

見る

http://Java-persistence-performance.blogspot.com/2010/08/batch-fetching-optimizing-object-graph.html

2
Kalpesh Soni

Personクラスに埋め込みキークラスがあるという例外を除き、まさにこの問題がありました。私自身の解決策は、クエリでそれらを結合することでした-削除

@Fetch(FetchMode.JOIN)

私の埋め込みIDクラス:

@Embeddable
public class MessageRecipientId implements Serializable {

    @ManyToOne(targetEntity = Message.class, fetch = FetchType.LAZY)
    @JoinColumn(name="messageId")
    private Message message;
    private String governmentId;

    public MessageRecipientId() {
    }

    public Message getMessage() {
        return message;
    }

    public void setMessage(Message message) {
        this.message = message;
    }

    public String getGovernmentId() {
        return governmentId;
    }

    public void setGovernmentId(String governmentId) {
        this.governmentId = governmentId;
    }

    public MessageRecipientId(Message message, GovernmentId governmentId) {
        this.message = message;
        this.governmentId = governmentId.getValue();
    }

}
1
Gunslinger