数式としてマッピングされた計算値(ランクなど)を含む検索結果のNHibernateキャッシングへの影響

StackOverflow https://stackoverflow.com/questions/1627707

質問

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)呼び出しは考案されており、実際に実際に発生することはありません)。

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

役に立ちましたか?

解決

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

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

NHibernateにはクエリとエンティティキャッシュ(実際には2つのエンティティキャッシュ-セッションキャッシュと< href = "http://nhforge.org/doc/nh/en/index.html#performance-cache" rel = "nofollow noreferrer">第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つのクエリを使用する必要があるのは少し面倒ですが、これが適切な方法でキャッシュを使用する唯一の方法のようです。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top