5

データベースに対してあまりにも多くの SQL 呼び出しを実行している Hibernate を使用して、アプリケーションのパフォーマンスを改善しようとしています。呼び出しを減らしてパフォーマンスを向上させるために、データのフェッチをグループ化できると思いますが、ここでは少し迷っています。サブセレクトとバッチフェッチに関するHibernateのドキュメントを見てきましたが、これは役に立ちますが、問題が完全に解消されるとは思いません。

以下の例では、軍隊の一部である兵士のリストの詳細を取得し、それを Web ページに表示する必要があります。

@Entity
public class Troop {
    @OneToMany(mappedBy="troop")
    public List<Soldier> getSoldiers() {
   ...
}

フェッチ戦略をサブセレクト、バッチ、または熱心に設定して、SQL ステートメントをあまり使わずに、この軍隊のすべての兵士の部分を取得するのは簡単です。

@Entity
public class Soldier {
    @Id
    String soldierId

    String firstName;
    String lastName;

    @OneToMany(mappedBy="address")
    public List<Soldier> getAddress() {
     ...
    @OneToMany(mappedBy="combatHistory")
    public List<Soldier> getCombatHistory() {
      ...
    @OneToMany(mappedBy="medicalHistory")
    public List<Soldier> getMedicalHistory() {
      ...
}

各兵士エンティティには、遅延ロードされる他のエンティティとの複数の 1 対多の関係があります。これらのコレクションを初期化し、それらから値を取得する必要があります。Soldier に 3 つの一対多の関係があり、Troop に 1,000 人の兵士がいる場合、3 x 1,000 の SQL 呼び出しになります!

呼び出し回数を減らしてこれを最適化する方法はありますか? 取得する必要があるsoldierIdsは既にわかっているので、エンティティを取得して、最初のレベルのキャッシュで利用できるようにすることはできますか?

たとえば、クエリを実行できます

from Address as a where a.soldierId in (...)
from CombatHistory as a where a.soldierId in (...)
from MedicalHistory as a where a.soldierId in (...)

Address、CombatHistory などのエンティティをキャッシュできる場合、各兵士内でコレクションにアクセスするときに SQL 選択は実行されません。これにより、兵士ごとにコレクションごとに 1 回 (3 x 1000) ではなく、コレクションごとに 1 回 (この場合は 3 回) に呼び出しの数が減ります。

この問題への対処に関するドキュメントはあまり見られなかったので、ヒントやアイデアは大歓迎です。これらのコレクションはセットではなくリストであるため、左結合フェッチを複数のコレクションで実行できないことに注意してください。そうしないと、Hibernate が例外を返します。

HibernateException: cannot simultaneously fetch multiple bags 
4

2 に答える 2

3

コレクションにアノテーション @Fetch(FetchMode.SUBSELECT) を使用することもできます。そのタイプの 1 つのコレクションを「タッチ」すると、すべてのコレクションが 1 つの SQL リクエストでフェッチされます。

@Entity
public class Country implements java.io.Serializable {

    private long id;
    private int version;
    private String country;
    private Set<City> cities = new HashSet<City>(0);

    @Fetch(FetchMode.SUBSELECT)
    @OneToMany(mappedBy = "country", cascade = CascadeType.ALL)
    public Set<City> getCities() {
        return cities;
    }


    ...
}

使用方法の例を次に示します。

    public List<Country> selectCountrySubSelect() {
        List<Country> list = getSession().createQuery("select c from Country c").list();
        // You don't have to initialize every collections
        // for (Country country : list) {
        // Hibernate.initialize(country.getCities());
        // }
        // but just "touch" one, and all will be initialized
        Hibernate.initialize(((Country) list.get(0)).getCities());
        return list;
    }

ログ:

DEBUG org.hibernate.engine.loading.internal.CollectionLoadContext.endLoadingCollections():  - 2 collections were found in result set for role: business.hb.Country.cities
DEBUG org.hibernate.engine.loading.internal.CollectionLoadContext.endLoadingCollection():  - Collection fully initialized: [business.hb.Country.cities#1]
DEBUG org.hibernate.engine.loading.internal.CollectionLoadContext.endLoadingCollection():  - Collection fully initialized: [business.hb.Country.cities#2]
DEBUG org.hibernate.engine.loading.internal.CollectionLoadContext.endLoadingCollections():  - 2 collections initialized for role: business.hb.Country.cities
DEBUG org.hibernate.engine.internal.StatefulPersistenceContext.initializeNonLazyCollections():  - Initializing non-lazy collections
于 2012-07-23T17:06:14.177 に答える
1

コレクションのすべてのアイテムをフェッチしても、休止状態はコレクションのすべてのアイテムがロードされていることを確認せず、コレクションを埋めません。ただし、兵士はセッション キャッシュにあるため、各コレクションを一度に 1 つずつ熱心にロードできます。

from Soldier s left join fetch s.Address where s.soldierId in (...)
from Soldier s left join fetch s.CombatHistory where s.soldierId in (...)
from Soldier s left join fetch s.MedicalHistory where s.soldierId in (...)

これにより、往復が 3 回に減ります。

于 2012-07-23T09:52:18.997 に答える