4

NHibernateを使用して、私は通常、Get()またはLoad()メソッドを使用して単一のレコードを照会します(プロキシが必要かどうかによって異なります)。

SomeEntity obj = session.Get<SomeEntity>(new PrimaryKeyId(1));

ここで、以下の例のように、このステートメントを2回実行すると、単体テストで実行されているクエリは1つだけになります。

SomeEntity obj1 = session.Get<SomeEntity>(new PrimaryKeyId(1));
SomeEntity obj2 = session.Get<SomeEntity>(new PrimaryKeyId(1));

ここまでは順調ですね。しかし、ICriteriaクエリを使用して同じオブジェクトを取得すると、奇妙な動作に気づきました。以下のコードを確認してください。最初のオブジェクトインスタンスを取得します。次に、プロパティの値を10(データベースの値は8)に変更し、別のインスタンスを取得して、最後に2番目のオブジェクトインスタンスの値を確認します。

//get the first object instance.
SomeEntity obj1 = session.CreateCriteria(typeof(SomeEntity))
                         .Add(Restrictions.Eq("Id", new PrimaryKeyId(1)))
                         .UniqueResult<SomeEntity>();

//the value in the database and the property is 8 at this point. Let's set it to 10.
obj1.SomeValue = 10;

//get the second object instance.
SomeEntity obj2 = session.CreateCriteria(typeof(SomeEntity))
                         .Add(Restrictions.Eq("Id", new PrimaryKeyId(1)))
                         .UniqueResult<SomeEntity>();

//check if the values match.
Assert.AreEqual(8, obj2.SomeValue);

新しいクエリでオブジェクトを要求したにもかかわらず、値がobj2の10であるため、何らかの理由でアサートが失敗します。面白いことに、ユニットテストの出力ウィンドウに従って実行されているまったく同じselectクエリが2つあります。私の質問:2番目のオブジェクトが第1レベルのキャッシュからフェッチされた場合、なぜ2つのクエリが実行されるのですか?

私は何かが足りないのですか、それともこれはバグですか?

よろしく、テッド

編集#1:NHibernate v2.1.2GAの使用編集#2:実行されている2つのクエリに関する説明を最後の段落に追加しました。

4

6 に答える 6

3

さて、NHibernateについてもっと多くを学んだので、この質問に自分で答えることができます。ICriteriaクエリは、NHibernateによってフェッチされたオブジェクトのリストを返します。NHibernateは、第1レベルのキャッシュ内のオブジェクトと1つずつ一致するまで、どのオブジェクトが返されるかを知りません。アイテムがすでに第1レベルのキャッシュマップにある場合、データベースから読み取られたアイテムは破棄されます。IDマップにない場合、アイテムは第1レベルのキャッシュに入れられます。

もう一つの「a-ha!」瞬間:データベースに5つの行があるときに初めてクエリを実行すると、すべての行がフェッチされ、第1レベルのキャッシュに入れられます。これで、時間の経過とともにさらに5つのレコードがテーブルに追加され、クエリを再実行します。これで10個のレコードすべてがフェッチされますが、NHibernateはそのうちの5個がすでにキャッシュにあることを確認し、後者の5個のレコードのみを追加します。したがって、基本的には5つのレコードを無料でフェッチしました(識別子をIDマップのオブジェクト識別子と一致させるためだけです)。

于 2011-08-12T14:52:31.933 に答える
2

Get / Loadは第1レベルのキャッシュを使用します。これが、第2レベルのデータベースの呼び出しが表示されない理由です。クエリは第1レベルのキャッシュを使用しません。ただし、第2レベルのキャッシュを使用するようにクエリを設定できます。詳細はこちらをご覧ください

更新発生する可能性が高いのは、クエリが2フェーズのロードを実行していることです。したがって、結果セットを取得するだけでなく、第1レベルのキャッシュをチェックして、そこにエンティティが存在するかどうかを確認します。含まれている場合は、キャッシュされたオブジェクトを返します。NHibernate.Loader.Loader.GetRowメソッドを参照してください。関連する行は次のとおりです。

//If the object is already loaded, return the loaded one
obj = session.GetEntityUsingInterceptor(key);
于 2011-01-28T15:55:39.350 に答える
1

AFAIK、' Get'(そして多分Load)だけが第1レベルのキャッシュを使用します。

Criteria APIを使用すると、第2レベルのキャッシュが有効になっていない限り、常にクエリがDBにヒットします。

編集:詳細については、こちらをご覧ください

于 2011-01-28T15:17:43.890 に答える
0

2番目のクエリが実行される理由はわかりませんが、NHibernateの予想される動作は、同じセッションからIDで同じオブジェクトを要求すると、最初のレベルのキャッシュを取得することです。

于 2011-01-28T15:08:42.147 に答える
0

NHibernateは、並行性の問題からユーザーを保護するために、おそらく最初のクエリと2番目のクエリの間に更新を発行しています。Frederikが指摘したように、常にGetキーを使用してオブジェクトを取得するために使用する必要があります。

私は興味があります、PrimaryKeyIdラッパーは何を追加していますか?

編集:

しかし、それは機能しています(私のお金はまだ選択前に更新されています)、この動作は仕様によるものです。メモリ内オブジェクトを破棄して、セッションからその新しいインスタンスをロードする場合は、Evict最初にセッションから元のオブジェクトをロードします。Refreshあなたが試すことができる方法もあります。

于 2011-01-28T15:31:25.530 に答える
0

私の理解では、Criteriaを使用する場合、基本的にNHibernateに「式に基づいて行をフィルタリングしたい」と言っています。そのように見た場合、NHibernateは、クエリが常にデータベースから同じフィルタリングされた行を返すかどうかを知る方法がないため、再度クエリを実行する必要があります。

また、ドキュメントにあるように、クエリキャッシングは第2レベルのキャッシングでのみ使用できます。

したがって、クエリキャッシュは常に第2レベルのキャッシュと組み合わせて使用​​する必要があります。

ここから

于 2011-01-28T16:14:13.047 に答える