2009-09-21 47 views
3

我想知道下面的這NHibernate的代碼可能會失敗什麼情況: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); 

斷言語句總是失敗。

如果我在SaveOrUpdate後手動調用session.Flush,然後選擇查詢成功,但我認爲我們不必手動調用flush?這是我的理解,NHibernate應該足夠聰明,以實現Foo已更新,所以第二個選擇查詢應該成功。

看着生成的SQL,它顯示第二個選擇查詢的SQL在第一個SaveOrUpdate的sql之前執行。

事實上,如果我包住整個方法在一個事務中,那麼它成功:

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

現在SaveOrUpdate的SQL將Linq.Where SQL前執行。這有點奇怪,因爲我不必在兩者之間進行交易。

這是怎麼回事?

+0

我覺得你的問題是誤導。我對NHibernate的工作模式單元有了一些想知道的知識,關於將變更與數據庫同步。你介意澄清一下嗎? – 2009-09-21 05:12:29

+0

真的所有我想要的是上面的測試成功:我希望能夠保存和可靠,簡單時尚重裝富。 – cbp 2009-09-21 11:55:00

回答

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(); 
    } 
} 
+0

有趣的是,即使在調用SaveOrUpdate後我沒有提交,添加一個事務也是可行的。不知何故,添加事務改變了NHibernate的行爲方式,我覺得有點奇怪。不過,我已經使用的TransactionScope(因爲這是一箇舊的應用程序),這意味着我不能使用的NHibernate的BeginTransaction(至少,我所遇到的各類問題,如果我不嘗試使用它)。但是,使用TransactionScope時,Assert失敗。看起來像我需要一種方法使NHibernate像我已經調用BeginTransaction一樣行事,但要改用TransactionScope。 – cbp 2009-09-21 04:12:54

+0

@cbp:令人不安。在不調用其Commit方法的情況下使用事務時要小心。該事務將被隱式回滾。這篇文章:http://forum.springframework.net/showthread.php?t=5351,可以幫助你。它涉及集成TransactionScope和ITransaction。順便提一句,你是否使用分析工具或檢查由NHibernate引擎創建的SQL日誌文件?如果是這樣,當你期望它或NHibernate的一級緩存機制存在問題時,保存/更新查詢不會發生。我懷疑前者是這種情況。 – 2009-09-21 04:21:57

+0

@cbp:我的意思是說,如果你正在尋找的SQL日誌,你可以準確地確定何時正在對數據庫執行的語句。正如您所看到的,實際發生時並不明顯。 – 2009-09-21 04:22:54

0

你可能有不正確的flushmode集。您需要將會話上的flushmode設置爲Auto,以便它在每次查詢之前自動刷新會話,否則您需要手動刷新會話以強制保存更改。

+0

FlushMode是默認的,我相信它是Auto? – cbp 2009-09-21 04:14:00

0

如果我手動調用調用Session.flush後 SaveOrUpdate,然後選擇查詢 成功。

首先:您甚至不需要調用SaveOrUpdate()。

這裏有一些事情用NH會議時,要記住:

  • 當你已載入從會議對象,會議將繼續跟蹤更改到該對象
  • 調用了Session.update(實體)只告訴NHibernate會話,它應該開始跟蹤對象,它並沒有去更改寫入數據庫

所以,你的情況,因爲你是從加載的對象session已經調用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); 
+0

就像我說的,是的,我可以手動調用Flush並且聲明成功。但是,似乎這不是使用NHibernate的方式:不應該有必要手動調用Flush無處不在 - 請參閱我的問題在這裏:http://stackoverflow.com/questions/1443214/flushing-in-nhibernate – cbp 2009-09-21 04:16:34

+0

不錯, Stefan在另一篇文章中已經說過我說過的話。你問「發生了什麼事?」不是「爲什麼我需要調用Flush()?」。也許重新閱讀http://www.nhforge.org/doc/nh/en/index.html#manipulatingdata-flushing收集您的想法。 – 2009-09-21 04:41:40

+0

好的,這個問題並沒有太多的衝動。問題是,NHibernate不拾起更新的foo.SomeProperty,所​​以第二個session.Linq.Where查詢返回0條記錄。 – cbp 2009-09-21 12:06:59

0

您必須關閉會話並在Assert之前創建一個現在的會話。

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(); 
    } 
} 
+0

如果我要做所有這些,只是打電話給Flush更容易些嗎?我認爲NHibernate應該足夠聰明以解決實體更新問題。 – cbp 2009-09-21 04:17:29

+0

如果您嘗試刷新並嘗試其他刷新方法....我在測試中使用此方法 – 2009-09-21 05:42:22

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); 
} 

還要注意的是,你做呼叫SaveUpdate,或SaveOrUpdate當你想保存對對象所做的更改,該Session已經追溯回數據庫。 NHibernate的作品不同於其他奧姆斯:如果跟蹤對象,那麼它會想出什麼時候發下來更改數據庫,你不需要告訴它這樣做。

1

「我想知道,下面的這NHibernate的代碼可能會失敗什麼情況:」我想你已經提供了至少一個回答自己的問題:當代碼被隱式事務中運行。請參閱this post from 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 <>。]我有許多這樣的測試,通過經常。