3

次の NHibernate コードがどのような状況で失敗する可能性があるのか​​ 疑問に思っています。

var session = NHibernateSessionManager.CurrentSession;

var foo = session.Linq<Foo>.ToList()[0];

foo.SomeProperty = "test";

session.SaveOrUpdate(foo);

var reloadedFoos = session.Linq<Foo>
                         .Where(x => x.SomeProperty == "test");

Assert.That(reloadedFoos.Count > 0);

Assert ステートメントは常に失敗します。

SaveOrUpdate の後に session.Flush を手動で呼び出すと、select クエリは成功しますが、flush を手動で呼び出す必要はないと思いましたか? NHibernate は Foo が更新されたことを認識するのに十分なほどスマートである必要があることを理解していたので、2 番目の選択クエリは成功するはずです。

生成された SQL を見ると、2 番目の選択クエリの SQL が最初の SaveOrUpdate の SQL の前に実行されているように見えます。

実際、メソッド全体をトランザクションでラップすると、成功します。

using(NHibernateSessionManager.CurrentSession.BeginTransaction()
{
    // Same code as above
}

これで、SaveOrUpdate の sql が Linq.Where sql の前に実行されます。間にトランザクションをコミットする必要さえないので、これは少し奇妙です。

何が起こっている?

4

6 に答える 6

3

NHibernate トランザクションを活用することをお勧めします。それらを使用しないと、NHibernate が SaveOrUpdate 呼び出しをいつ発行するかを決定する方法がない可能性は十分にあります。

トランザクションを使用すると、読み取り専用ステートメントでさえパフォーマンスが向上することがわかります。詳細については、 http://nhprof.com/Learn/Alert?name=DoNotUseImplicitTransactionsを参照してください。

例えば:

using(var session = NHibernateSessionManager.CurrentSession)
{
  using(var transaction = session.BeginTransaction())
  {
    var foo = session.Linq<Foo>.ToList()[0];

    foo.SomeProperty = "test";

    session.SaveOrUpdate(foo);
    transaction.Commit();
  }
}
于 2009-09-21T00:49:37.303 に答える
3

NHibernate が「スマート」であるためには、トランザクションが必要であることに注意してください。

仕組みは次のとおりです。

var session = NHibernateSessionManager.CurrentSession;
using(NHibernateSessionManager.CurrentSession.BeginTransaction()) {
    var foo = session.Linq<Foo>.ToList()[0];
    foo.SomeProperty = "test";
    var reloadedFoos = session.Linq<Foo>()
        .Where(x => x.SomeProperty == "test");
    Assert.That(reloadedFoos.Count > 0);
}

がすでにデータベースに追跡しているオブジェクトに加えた変更を保存する場合は、 、、またはを呼び出さないことにも注意してください。NHibernate の動作は他の ORM とは異なります。オブジェクトを追跡している場合、データベースに変更をいつ送信するかを判断し、そのように指示する必要はありません。SaveUpdateSaveOrUpdateSession

于 2009-09-21T12:16:58.607 に答える
1

「どのような状況で次の NHibernate コードが失敗する可能性があるのか​​ 疑問に思っています:」コードが暗黙のトランザクション内で実行される場合、あなた自身の質問に対して少なくとも 1 つの回答を提供したと思います。Ayende のこの投稿を参照してください。暗黙のトランザクション内での一貫性のない動作について言及しています。テストドライバーがラッピングトランザクションを提供することを除いて、コードに似た多くの単体テストがあります。

[Test]
public void Can_Update_Account() {
        Account account = PersistenceContext.Get<Account>(TEST_ACCOUNT_ID);

        string accountNumber = RandomString(10);
        account.AccountNumber = accountNumber;

        Account account1 = PersistenceContext.GetAll<Account>().Where(x => x.AccountNumber == accountNumber).SingleOrDefault();
        Account account2 = PersistenceContext.Get<Account>(account.Id);
        Assert.AreEqual(account.Id, account1.Id);
        Assert.AreEqual(accountNumber, account2.AccountNumber);
    }

[GetAll<>() は、Linq<> のシン ラッパーです。] 私は定期的に合格するそのようなテストをたくさん持っています。

于 2009-09-21T16:56:56.730 に答える
0

セッションを閉じて、アサートの前に今すぐセッションを作成する必要があります。

using(var session = NHibernateSessionManager.CurrentSession)
{
  using(var tx = session.BeginTransaction())
  {
    var foo = session.Linq<Foo>.ToList()[0];
    foo.SomeProperty = "test";
    session.SaveOrUpdate(foo);  
    tx.Commit();
  }
}

//create a new session here, the code depend if you use RhinoCommons (like me), no Rhino

using(var session = NHibernateSessionManager.CurrentSession)
{
  using(var tx = session.BeginTransaction())
  {
    var reloadedFoos = session.Linq<Foo>
            .Where(x => x.SomeProperty == "test");
    Assert.That(reloadedFoos.Count > 0);
    tx.Commit();
  }
}
于 2009-09-21T03:39:50.490 に答える
0

SaveOrUpdateの後に手動でsession.Flushを呼び出すと、selectクエリは成功します。

まず、SaveOrUpdate()を呼び出す必要はありません。

NHセッションを使用するときに覚えておくべきことがいくつかあります。

  • セッションからオブジェクトをロードすると、セッションは引き続きそのオブジェクトへの変更を追跡します
  • session.Update(entity)を呼び出すと、NHibernateセッションにオブジェクトの追跡を開始するように指示するだけで、データベースに変更を書き込みません。

したがって、あなたの場合、すでにセッションからオブジェクトをロードしているため、session.Update()を呼び出しても、オブジェクトはすでに追跡されているため、何も実行されません。次の手順を実行するだけで、データベースを強制的に更新できます。

var session = NHibernateSessionManager.CurrentSession;
var foo = session.Linq<Foo>.ToList()[0];
foo.SomeProperty = "test";

session.Flush();

var reloadedFoos = session.Linq<Foo>
                         .Where(x => x.SomeProperty == "test");
Assert.That(reloadedFoos.Count > 0);
于 2009-09-21T01:20:31.813 に答える
0

おそらく、flushmode の設定が正しくありません。すべてのクエリの前にセッションを自動的にフラッシュするには、セッションのフラッシュモードを Auto に設定する必要があります。それ以外の場合は、セッションを手動でフラッシュして、変更を強制的に保存する必要があります。

于 2009-09-21T00:50:05.790 に答える