12

私はこれをやろうとしています:

//...
class Person {
    @ManyToMany(fetch = FetchType.EAGER)
    @Fetch(FetchMode.JOIN)
    private Set<Group> groups;
//...
}

personRepository.findAll();Spring JPA リポジトリを使用すると、セットがない場合と同じように、n+1 個のクエリが生成さ@Fetchれます。(最初に 1 つのクエリですべての人を取得し、次に 1 人あたり 1 つのクエリでグループを取得します)。

@Fetch(FetchMode.SUBSELECT) ただし、作品を使用してください!2 つのクエリのみが生成されます。(すべての人に 1 つ、次にグループに 1 つ)。そのため、 hibernate はいくつかのフェッチ パラメータに反応しますが、JOIN.

EAGERまた、フェッチを削除しようとしましたが、うまくいきませんでした。

//...
class Person {
    @ManyToMany()
    @Fetch(FetchMode.JOIN)
    private Set<Group> groups;
//...
}

私は Spring JPA を使用しています。これが私のリポジトリのコードです。

public interface PersonRepository extends JpaRepository<Person, Long> {
}

JOIN は Spring JPA では機能しませんか、それとも何か間違っていますか?

4

2 に答える 2

22

あなたの問題を読むために多くのフォーラムやブログに目を通します(ここに投稿する前にそれを行ったかもしれません)私もそう思います

@Fetch(FetchMode.JOIN) は、Query インターフェイス (例: session.createQuery()) を使用する場合は無視されますが、Criteria インターフェイスを使用する場合は適切に使用されます。

これは事実上、解決されていない Hibernate のバグです。多くのアプリケーションが Query インターフェイスを使用しており、Criteria インターフェイスに簡単に移行できないため、残念です。

Query インターフェイスを使用する場合は、常に JOIN FETCH ステートメントを手動で HQL に追加する必要があります。

参考文献 Hibernate Forum Spring Forum 同様の質問 1

于 2013-09-27T10:42:41.633 に答える
9

@Fetch(FetchMode.JOIN)また、JPA を使用している場合は作業できませんでしたが(休止状態の Criteria API を使用している場合は正常に動作します)、理由を説明する例も見つかりませんでしたが、いくつかの回避策を考えることができます。

グループを熱心にロードする最も簡単な方法は、JPQL を使用することです。

public interface PersonRepository extends JpaRepository<Person, String>{
  @Query(value = "select distinct p from Person p left join fetch p.groups")
  List<Person> getAllPersons();
}

spring-data-jpa を使用しているため、 を使用してグループを熱心にロードすることもできますSpecification。(1.4.x 以降では、null を返す仕様をチェーンできます)。

final Specification<Person> fetchGroups = new Specification<Person>() {
    @Override
    public Predicate toPredicate(Root<Person> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
        root.fetch("groups", JoinType.LEFT);
        query.distinct(true);
        return null;
    }
};

これらのいずれも選択できない場合、最善の策はおそらく を使用すること@Fetch(FetchMode.SUBSELECT)です。

@Fetch(FetchMode.SELECT)と組み合わせて使うのも一つの手@BatchSizeです。@BatchSizen+1 クエリの問題を解決するのに役立ちます。バッチ サイズを微調整することで、実行されるクエリの量を CEIL(n/batch_size)+1 に減らすことができます。

@Entity
@Table(name = "persons")
public class Person {
  @Id
  String name;

  @ManyToMany(fetch = FetchType.EAGER)
  @BatchSize(size = 20)
  Set<Group> groups = new HashSet<>();
}

@Entity
@Table(name = "groups")
public class Group {
  @Id
  String name;

  @ManyToMany(mappedBy = "groups", fetch = FetchType.LAZY)
  Set<Person> persons = new HashSet<>();
}

public interface PersonRepository extends JpaRepository<Person, String>{}

personRepository.findAll();このマッピングは、 10 人を含み、5 に設定されたデータベースで実行すると、次の sql になります@BatchSize

Hibernate: 
select
    person0_.name as name1_ 
from
    persons person0_
Hibernate: 
select
    groups0_.persons_name as persons1_1_1_,
    groups0_.groups_name as groups2_1_,
    group1_.name as name0_0_ 
from
    persons_groups groups0_ 
inner join
    groups group1_ 
        on groups0_.groups_name=group1_.name 
where
    groups0_.persons_name in (
        ?, ?, ?, ?, ?
    )
Hibernate: 
select
    groups0_.persons_name as persons1_1_1_,
    groups0_.groups_name as groups2_1_,
    group1_.name as name0_0_ 
from
    persons_groups groups0_ 
inner join
    groups group1_ 
        on groups0_.groups_name=group1_.name 
where
    groups0_.persons_name in (
        ?, ?, ?, ?, ?
    )

@BatchSizeでマップされたコレクションでも機能することに注意してくださいFetchType.LAZY

于 2013-09-27T23:03:11.713 に答える