2011-08-03 6 views
2

ORMにNhibernateを使用しています。なぜNHibernateは私のデータを取り出すのですか

私は、ControlDetailと1対多の関係を持つ(つまり、コントロールには多くのcontrolDetailsがあります)クラス "Control"を持っています。制御XMLで

は、それがそうでなければ、それは怠惰な負荷だろうコントロールのcontroldetailsに語っていない限り、私は信じているように、次の

<bag name="ControlDetails" lazy="true" access="property" order-by="SortOrder asc" cascade="all-delete-orphan" 
    table="ControlDetail"> 
    <key column="ControlID"/> 
    <one-to-many class="ControlDetail"/> 
</bag> 

を持っているconfigを入力します。

私はNHProfを実行して、パフォーマンス上の問題を解決しています。これらのクラスの周りにN + 1の問題を特定しました。

私たちはリポジトリのDA層を使用しています。私は、必要なときにデータを熱心に取得し、これを思いつく方法を追加できるかどうかを試しました。

public T GetById<T>(Int32 id, List<string> fetch) where T : BaseObject 
{ 
    T retObj = null; 
    ISession session = EnsureCurrentSession(); 
    { 
     ICriteria criteria = session.CreateCriteria(typeof (T)); 
     criteria.SetCacheable(true); 
     criteria.Add(Expression.Eq("Id", id)); 

     foreach(var toFetch in fetch) 
     { 
      criteria.SetFetchMode(toFetch, FetchMode.Eager); 
     } 

     retObj = criteria.List<T>().FirstOrDefault(); 
    } 

    return retObj; 
} 

*注:私はリポジトリを設置しているかのファンではないが、私はそう、我々は今のところ、このパターンに固執する必要がプロジェクトに来る前にそれが行われました。

私はそう

public Control GetByIDWithDetail(int controlID) 
{ 
    return DataRepository.Instance.GetById<Control>(controlID, new List<string>() {"ControlDetail"}); 
} 

私はGetByID方法をデバッグし、retObjを見てみると不思議な私も設定はsetFetchModeずに気づいたが、私は(ControlDetailsリストが取り込まれていることがわかりますように、このメソッドを呼び出しますリストはこれでも修正NHProfと

は、次の行

List<ControlDetail> details = control.ControlDetails.ToList(); 

何exactlで選択N + 1問題を識別)が取り込まれていましたyは私が行方不明ですし、どのように私は、このN + 1を停止するが、それでもcontrolDetailsリスト上

EDITを繰り返すことができます:XMLのコンフィグがそうのように見える(少し小さくするように編集)

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="DomainObjects" assembly="DomainObjects"> 
    <class name="Control" lazy="false" table="Control" optimistic-lock="version" select-before-update="true" > 
     <id name="Id" type="int" column="ControlID" access="property"> 
      <generator class="native" /> 
     </id> 
    <version name="Version" column="Version" /> 
     <property name="AdministrativeControl" column="AdministrativeControl" access="property" not-null="true" /> 
    <property name="Description" column="ControlDescription" access="property" /> 
    <property name="Title" column="Title" access="property" /> 
    <property name="CountOfChildControls" access="property" formula="(select count(*) from Control where Control.ParentControlID = ControlID)" /> 

    <bag name="ControlDetails" lazy="true" access="property" order-by="SortOrder asc" cascade="all-delete-orphan" 
     table="ControlDetail"> 
     <key column="ControlID" /> 
     <one-to-many class="ControlDetail" /> 
    </bag> 

    </class> 
</hibernate-mapping> 

この

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="DomainObjects" assembly="DomainObjects"> 
    <class name="ControlDetail" lazy="false" table="ControlDetail" select-before-update="true" optimistic-lock="version"> 
     <id name="Id" type="int" column="ControlDetailID" access="property"> 
      <generator class="native" /> 
     </id> 
    <version name="Version" column="Version" /> 
    <property name="Description" column="Description" access="property" not-null="true" /> 
    <property name="Title" column="Title" access="property" /> 

    <many-to-one name="Control" lazy="false" class="Control" column="ControlID" access="property"/> 
    </class> 
</hibernate-mapping> 
+0

「コントロール」は、最初のレベルのキャッシュから来ている可能性が高いですが、関係は、親とキャッシュされるように設定されていないため、遅延ロードされています。 NHProfを取得し、キャッシュヒット対クエリを見てください。 – Phill

+0

nhprofは、クエリキャッシュヒット/ミス/プットカウントで0を示し、2番目のレベルのキャッシュヒット/ミス/プットカウントでも0を示します。これを修正するにはどうすればよいですか? –

+0

ControlとControlDetailの両方で0ヒットですか?または単にControlDetail? – Phill

答えて

5

eager fetchingとeager loadingの間には大きな違いがあります。フェッチとは、同じクエリを実行することです。いくつかの副作用があり、それを壊す可能性があります。熱心なローディングとはNHに最初にアクセスされるまで待つのではなく、すぐにロードすることを意味します。追加のクエリを使用してロードされているため、N + 1の問題が発生します。

プロパティはControlDetailsと呼ばれていますが、ControlDetailを引数として渡しているため、NHはおそらく熱心にフェッチしません。

これは、N + 1の問題を回避する良い方法ではありません。代わりにバッチサイズを使用してください。これは、アプリケーションに対して完全に透過的であり、指定された係数(5〜50の値は意味があり、使用するものがわからない場合は10を使用します)によるクエリの量を減らします。

+1

+1、とりわけページングでの熱心なフェッチの副作用... – mathieu

+1

@マチュー:まさに。そして、同じクエリで複数のコレクションを引き出すことを熱心に試みてください。 DistinctRootEntityTransformerがありますが、 "DistinctCollectionItemTransformer"はありません。ちょうどすべてが倍増します。私はまた、クエリで多すぎる列にいくつかの奇妙な問題が発生しました。私は本当にそれを全く使わなくなった。 –

1

一つのオプションは、あなたがする必要があるすべてがオンに追加することで、選択のn + 1つの問題を軽減遅延ロードを維持し、積極的なロード....

を忘れることができますあなたのXML

<bag name="ControlDetails" batch-size="25" ..> 

への電子の単純な属性batch-size私はまた、あなたのマッピングでlazy="true"を持っていることに気づいたあなたはfalseに変更してみたのですか?

関連する問題