6

を使用してデータベースから更新されたレコードを取得しようとしていますQueryOver。私のコードは最初にエンティティを作成してデータベースに保存し、次に同じレコードがデータベースで外部的に更新されます(他のプログラムから、手動で、または他のマシンで実行されている同じプログラムから)、queryOver変更されたフィールドによるフィルタリングを呼び出すと、クエリは記録しますが、最新の変更はありません。

これは私のコードです:

//create the entity and save in database
MyEntity myEntity = CreateDummyEntity();
myEntity.Name = "new_name";

MyService.SaveEntity(myEntity);

// now the entity is updated externally changing the name property with the 
// "modified_name" value (for example manually in TOAD, SQL Server,etc..)

//get the entity with QueryOver
var result = NhibernateHelper.Session
                 .QueryOver<MyEntity>()
                 .Where(param => param.Name == "modified_name")
                 .List<T>();

前のステートメントは、レコードが 1 つしかない (良い) コレクションを取得しますが、「 modified_name」ではなく古い値で確立された name プロパティを使用します。

この動作を修正するにはどうすればよいですか? 第 1 レベルのキャッシュが気になりますか? 同じ問題が発生します

CreateCriteria<T>();

私の NhibernateHelper のセッションは、アプリケーション フレームワークの要件により、いつでも閉じられません。session.Save() に関連付けられた各コミットに対してのみトランザクションが作成されます。新しいセッションを開いてクエリを実行すると、明らかにデータベースから最新の変更が取得されますが、このアプローチは設計要件では許可されていません。

また、NHibernate SQL 出力で、WHERE 句を含む選択が実行されていることを確認しました (したがって、Nhibernate はデータベースにヒットします) が、返されたオブジェクトを更新しません!!!!

アップデート

session.Save を呼び出した後の SaveEntity のコードは次のとおりです。Commit メソッドの呼び出しが行われます。

public virtual void Commit() 
{ 
  try 
  { 
    this.session.Flush(); 
    this.transaction.Commit();
  } 
  catch 
  { 
    this.transaction.Rollback(); 
    throw; 
  } 
  finally 
  { 
    this.transaction = this.session.BeginTransaction();
  } 
}

SaveEntity のために NHibernate によって生成された SQL:

NHibernate: INSERT INTO MYCOMPANY.MYENTITY (NAME) VALUES (:p0);:p0 = 'new_name'. 

NHibernate によって QueryOver 用に生成された SQL:

NHibernate: SELECT this_.NAME as NAME26_0_ 
            FROM MYCOMPANY.MYENTITY this_ 
            WHERE this_.NAME = :p0;:p0 = 'modified_name' [Type: String (0)]. 

会社の機密ポリシーにより、クエリが変更されました。

大変助かりました。

4

6 に答える 6

4

私の知る限り、いくつかのオプションがあります。

  • の代わりにIStatelessSession呼び出して、セッションをsessionFactory.OpenStatelesSession()sessionFactory.OpenSession()
  • Session.Evict(myEntity)DB にエンティティを永続化した後に実行する
  • Session.Clear()あなたの前に実行するQueryOver
  • CacheModeあなたのセッションのをあなたのIgnore, Put or Refresh前に設定しますQueryOver(それをテストしたことはありません)

選択は、長時間実行されているセッションの使用状況に依存すると思います(これは、私見では、解決策よりも多くの問題をもたらすようです)

于 2013-04-04T08:21:28.617 に答える
2

を呼び出しsession.Save(myEntity) ても、変更がすぐに DB に永続化されるわけではありません*。これらの変更はsession.Flush()、フレームワーク自体または自分で が呼び出されたときに保持されます。フラッシュとそれが呼び出されるタイミングの詳細については、この質問とフラッシュに関するnhibernate のドキュメントを参照してください。

また、クエリを実行しても、第 1 レベルのキャッシュはヒットしません。これは、第 1 レベルのキャッシュがGetandLoadでのみ機能するためです。つまり、of 1 がすでにロードされているsession.Get<MyEntity>(1)場合は第 1 レベルのキャッシュにヒットしますが、そうではありません。MyEntityidsession.QueryOver<MyEntity>().Where(x => x.id == 1)

NHibernate のキャッシュ機能の詳細については、Ayende Rahien によるこの投稿を参照してください。

要約すると、次の 2 つのオプションがあります。

  1. SaveEntityメソッド内でトランザクションを使用します。つまり、

    using (var transaction = Helper.Session.BeginTransaction())
    {
      Helper.Session.Save(myEntity);
      transaction.Commit();
    }
    
  2. session.Flush()メソッド内で呼び出すSaveEntity、つまり

      Helper.Session.Save(myEntity);
      Helper.Session.Flush();
    

最初のオプションは、ほぼすべてのシナリオで最適です。

*私が知っているこのルールの唯一の例外はIdentity、ID ジェネレータ タイプとして使用する場合です。

于 2013-04-04T08:42:16.660 に答える
1

最後のクエリを次のように変更してみてください。

 var result = NhibernateHelper.Session
             .QueryOver<MyEntity>()
             .CacheMode(CacheMode.Refresh)
             .Where(param => param.Name == "modified_name")

それでもうまくいかない場合は、クエリの後にこれを追加してみてください。

NhibernateHelper.Session.Refresh(result);
于 2013-04-04T15:29:38.487 に答える
0

Session.Clear()ここの人は強すぎるので 呼びたくない。
一方、Session.Evict()オブジェクトが事前にわかっていない場合は、適用できないように見える場合があります。
実はまだ使えます。
最初にクエリを使用してキャッシュされたオブジェクトを取得し、次にEvict()それらを呼び出す必要があります。そして、同じクエリを再度呼び出して、新しいオブジェクトを再度取得します。
オブジェクトが最初からキャッシュされていない場合、このアプローチは少し非効率的です - 実際には2つの「新鮮な」クエリがあるため - しかし、その欠点についてはあまりやるべきことがないようです...
ちなみに、Evict()null引数を受け入れます例外なく - これは、クエリされたオブジェクトが実際に DB に存在しない場合に役立ちます。

var cachedObjects = NhibernateHelper.Session
             .QueryOver<MyEntity>()
             .Where(param => param.Name == "modified_name")
             .List<T>();

foreach (var obj in cachedObjects) 
    NhibernateHelper.Session.Evict(obj);

var freshObjects = NhibernateHelper.Session
             .QueryOver<MyEntity>()
             .Where(param => param.Name == "modified_name")
             .List<T>()
于 2016-12-30T20:38:04.570 に答える
0

私は非常によく似たものを手に入れており、NHibernate のデバッグを試みました。私のシナリオでは、セッションは、関連するコレクション (cascade:all) にいくつかの子を持つオブジェクトを作成し、次に を呼び出しますISession.Flush()。レコードは DB に書き込まれ、セッションは閉じずに続行する必要があります。その間、別の 2 つの子レコードが DB に書き込まれ、コミットされます。QueryOver元のセッションが withを使用してグラフの再ロードを試みると、JoinAlias、生成された SQL ステートメントは完全に正常に見え、行は正しく返されていますが、これらの新しい子を受け取る必要があるコレクションは、セッション内で既に初期化されていることがわかり (そうあるべきです)、それに基づいて NH が決定しますそれぞれの行を完全に無視する何らかの理由。コレクションがすでに「初期化済み」とマークされている場合、クエリから再ロードする必要はないという NH の想定は間違っていると思います。NHibernate の内部構造に詳しい人がこれについて意見を述べてくれれば、それは素晴らしいことです。

于 2017-04-20T21:32:05.440 に答える