2

NHibernateで数式を使用して計算プロパティを定義する場合、特にクエリのキャッシュに関して、数式がクエリの制限に応じて結果を変える場合、どのような影響がありますか?

具体的には、次の単純なC#クラスについて考えてみます。

public class Entity
{
    public Entity() { }
    public virtual int Id { get; protected set; }
    public virtual string Key { get; protected set; }
    public virtual string Value { get; protected set; }
    public virtual int Rank { get; protected set; }
}

次の単純なNHibernateマッピングでマッピングされます。

<class name="Entity" mutable="false">
    <id name="Id">
        <generator class="native">
    </id>
    <property name="Key"/>
    <property name="Value"/>
    <property name="Rank" formula="row_number() over(order by value)">
</class>

hibernate.cache.use_query_cacheオプションがに設定されたセッションファクトリでtrue実行し、次の方法でクエリを実行します。

ICriteria criteria = session.CreateCriteria(typeof(Entity));
criteria.SetCacheable(true);
criteria.SetCacheRegion("SearchResults");
IList<Entity> queryResult1 = criteria.List<Entity>();

criteria = session.CreateCriteria(typeof(Entity));
criteria.SetCacheable(true);
criteria.SetCacheRegion("SearchResults");
criteria.Add(Restrictions.Like("Key", "a", MatchMode.Anywhere));
IList<Entity> queryResult2 = criteria.List<Entity>();

Entity directResult = session.Load<Entity>(id);

NHibernateは、返されたエンティティに対して合理的な方法で動作しますか?または、あるキャッシュされたクエリの「ランク」値が、クエリキャッシュのために、別のクエリのランク値を汚染する可能性がありますか?NHibernateマッピングでこのような式を使用する場合、他に懸念事項はありますか?

編集:

私の特定のケースでは、「エンティティ」は一流のビジネスエンティティではなく、一種のメタエンティティであることも注目に値するかもしれません。これは、他のファーストクラスエンティティのインデックス付きデータベースビューにマップされ、検索専用に使用されます(session.Load(id)呼び出しは考案されており、実際には発生しないはずです)。

そして、私が思うに、キャッシングに影響がある場合、潜在的な問題を回避するために、同様のユースケースに対してどのような代替案が存在する可能性がありますか

4

1 に答える 1

2

さらに実験した後:はい、一貫性のない結果をもたらす可能性のあるキャッシュへの影響があります。NHibernateは、式が同じ識別子を持つエンティティ結果のクエリ間で値を変更する可能性があることを自動的に知ることはできません(変更されないと想定します)。

問題のクラスマッピングを使用すると、ランクが残りのエンティティデータとともに保存されます。これにより、後続のクエリが、実行中のクエリではなく、他のクエリからランク値を返すことになり、期待どおりに連続していないランクを持つ可能性があります。

NHibernateには個別のクエリキャッシュとエンティティキャッシュがあり(実際には2つのエンティティキャッシュ(セッションキャッシュ第2レベルのエンティティキャッシュ)があります)、影響はどちらが使用されているかによって異なります。

クエリキャッシュが有効になっていない場合、同じセッション内で結果を共有するがランクが異なる2つの異なるクエリを実行すると、誤ったランク値を受け取る可能性があります。この場合、同じセッションの2番目のクエリは、最初のクエリのセッションにすでに存在するエンティティデータを上書きしないため(その作業単位で変更されている可能性があるため)、返されるランク値は最初のクエリと同じになります。 2番目のクエリからの実際のランクではなくクエリ。最初のクエリの結果を削除すると、この問題回避できます(ただし、推奨される解決策ではありません。以下を参照してください)。

クエリキャッシュ有効になっている場合、別のランクの結果が得られた他のクエリが実行された後に同じクエリを繰り返すと、誤ったランク値を受け取る可能性もあります。この場合、最初のクエリ実行により、結果識別子がクエリキャッシュに追加され、エンティティ(ランク付き)がエンティティキャッシュに追加されます。インターリーブされたクエリ(別のセッションで実行された場合)により、エンティティキャッシュにエンティティとともに保存されたランク値が変更される可能性があります。最初のクエリが再実行されると、キャッシュされた識別子を使用して、(ランクが変更された)キャッシュされたエンティティが検索されます。


この問題は、エンティティを変更して、エンティティの永続化された値のみを含める(つまり、ランクを除外する)ことで完全に対処できます。次に、クエリの場合、プロジェクションを使用して、そのクエリの識別子とランクを抽出します。

ICriteria criteria = session.CreateCriteria(typeof(Entity));
criteria.SetCacheable(true);
criteria.SetCacheRegion("SearchResults");
criteria.SetProjection
    (Projections.Id(), 
     Projections.SqlProjection("row_number() over(order by value) as Rank",
                               new[] { "Rank" },
                               new[] { NHibernateUtil.Int32 }));

この場合、ランクは値型であるため、クエリキャッシュは、その特定のクエリのクエリ結果識別子と一緒にランクを格納します。次に、2番目のクエリを使用して、投影された識別子を使用してエンティティ値を検索します。N+1トリッキーな部分は、エンティティクエリを実行するときに型の問題を回避する必要がありEntity、そのクエリのとそれに関連するランクを結合するために別のデータ構造を作成する必要があることです。

1つのクエリではなく、2つの別々のクエリを使用する必要があるのは少し面倒ですが、これがキャッシュを適切な方法で使用する唯一の方法のようです。

于 2010-01-25T18:21:12.170 に答える