1. 程式人生 > 實用技巧 >NHibernate之(21):探索物件狀態

NHibernate之(21):探索物件狀態

本節內容

引入

在程式執行過程中使用物件的方式對資料庫進行操作,這必然會產生一系列的持久化類的例項物件。這些物件可能是剛剛建立並準備儲存的,也可能是從資料庫中查詢的,為了區分這些物件,根據物件和當前會話的關聯狀態,我們可以把物件分為三種:

瞬時物件:物件剛剛建立。該物件在資料庫中沒有記錄,也不在ISession快取中。如果該物件是自動生成主鍵,則該物件的物件識別符號為空。

持久化物件:物件已經通過NHibernate進行了持久化,資料庫中已經存在對應的記錄。如果該物件是自動生成主鍵,則該物件的物件識別符號已被賦值。

託管物件:該物件是經過NHibernate儲存過或者從資料庫中取出的,但是與之關聯的ISession已經關閉。雖然它有物件識別符號且資料庫中存在對應記錄,但是已經不再被NHibernate管理。

物件狀態

NHibernate提供了物件狀態管理的功能,支援三種物件狀態:瞬時態(Transient)、持久態(Persistent)、託管態(Detached)。

1.瞬時態(Transient)

物件剛剛建立,還沒有來及和ISession關聯的狀態。這時瞬時物件不會被持久化到資料庫中,也不會被賦上識別符號。如果不使用則被GC銷燬。ISession介面可以將其轉換為持久狀態。

這像這樣,剛剛建立了一個Customer物件,是一個瞬時態物件:

var customer = new Customer() { Firstname = "YJing", Lastname = "Lee"  };

2.持久態(Persistent)

剛被儲存的或剛從資料庫中載入的。物件僅在相關聯的ISession生命週期內有效,在資料庫中有相應記錄並有識別符號。物件例項由NHibernate框架管理,如果有任何改動,在當然操作提交時,與資料庫同步,即將物件儲存更新到資料庫中。

3.託管態(Detached)

持久物件關聯的ISession關閉後,這個物件在ISession中脫離了關係,就是託管態了,託管物件仍然有持久物件的所有屬性,對託管物件的引用仍然有效的,我們可以繼續修改它。如果把這個物件重新關聯到ISession上,則再次轉變為持久態,在託管時期的修改會被持久化到資料庫中。

物件狀態轉換

在同步資料庫的情況下執行下面的語句可以轉換物件的狀態。

測試驗證物件

ISession.Contains(object):檢查ISession中是否包含指定例項

重新設定ISession

private void ResetSession()
{
    if (_session.IsOpen)
        _session.Close();
    _session = _sessionManager.GetSession();
    _transaction.Session = _session;
}

1.瞬時態轉換持久態

方法一:ISession.Save():儲存指定例項。

[Test]
public void TransientConvertPersistentTest()
{
    //瞬時態物件
    var customer = new Customer() { Firstname = "YJidng", Lastname = "Lee" };
    Assert.IsFalse(_session.Contains(customer));
    //仍然是瞬時態,CustomerId屬性值為空

    //關聯ISession儲存到資料庫中
    _session.Save(customer);
    //變為持久態,由於表中CustomerId欄位自動增長的,儲存資料庫,CustomerId欄位自動加一
    //經過NHibernate型別轉換後返回CustomerId屬性值,保證資料庫與例項物件同步
    Assert.IsTrue(_session.Contains(customer));
}

方法二:ISession.SaveOrUpdate():分配新標識儲存瞬時態物件。

2.持久態轉換託管態

方法一:ISession.Evict(object):從當前ISession中刪除指定例項

[Test]
public void PersistentConvertDetachedEvictTest()
{
    Customer customer = _transaction.GetCustomerById(1);
    Assert.IsTrue(_session.Contains(customer));
    _session.Evict(customer);
    Assert.IsFalse(_session.Contains(customer));
}

方法二:ISession.Close():關閉當前ISession

[Test]
public void PersistentConvertDetachedCloseTest()
{
    Customer customer = _transaction.GetCustomerById(1);
    Assert.IsTrue(_session.Contains(customer));
    ResetSession();
    Assert.IsFalse(_session.Contains(customer));
}

3.託管態轉換持久態

方法一:ISession.Update():更新指定例項。

[Test]
public void DetachedConvertPersistentUpdateTest()
{
    Customer customer = _transaction.GetCustomerById(1);
    //持久態物件
    Assert.IsTrue(_session.Contains(customer));
    //重新設定ISession
    ResetSession();
    Assert.IsFalse(_session.Contains(customer));
    //託管態物件
    //在託管態下可繼續被修改
    customer.Firstname += "CnBlogs";
    _transaction.UpdateCustomerTransaction(customer);
    //轉變為持久態物件
    Assert.IsTrue(_session.Contains(customer));
}

看看這個例子:在託管時期的修改會被持久化到資料庫中;

注意:NHibernate如何知道重新關聯的物件是不是“髒的(修改過的)”?如果是新的ISession,ISession就不能與物件初值來比較這個物件是不是“髒的”,我們在對映檔案中定義<id>元素和<version>元素的unsaved-value屬性,NHibernate就可以自己判斷了。

[Test]
public void DetachedConvertPersistentUpdateAllTest()
{
    Customer customer = _transaction.GetCustomerById(1);
    //持久態物件
    customer.Firstname += "YJingLee";
    Assert.IsTrue(_session.Contains(customer));
    //重新設定ISession
    ResetSession();
    Assert.IsFalse(_session.Contains(customer));
    //託管態物件
    //在託管態下可繼續被修改
    customer.Firstname += "CnBlogs";
    //這時一起更新
    _transaction.UpdateCustomerTransaction(customer);
    //轉變為持久態物件
    Assert.IsTrue(_session.Contains(customer));
}

這個加上一個鎖:如果在託管時期沒有修改,就不執行更新語句,只轉換為持久態,下面的例子如果在託管時期修改物件,執行更新語句。

[Test]
public void DetachedConvertPersistentUpdateLockTest()
{
    Customer customer = _transaction.GetCustomerById(1);
    Assert.IsTrue(_session.Contains(customer));
    ResetSession();
    Assert.IsFalse(_session.Contains(customer));
    //鎖
    _session.Lock(customer, NHibernate.LockMode.None);
    //如果在託管時期沒有修改,就不執行更新語句,只轉換為持久態
    //customer.Firstname += "CnBlogs";
    _transaction.UpdateCustomerTransaction(customer);
    Assert.IsTrue(_session.Contains(customer));
}

方法二:ISession.Merge():合併指定例項。不必考慮ISession狀態,ISession中存在相同標識的持久化物件時,NHibernate便會根據使用者給出的物件狀態覆蓋原有的持久化例項狀態。

方法三:ISession.SaveOrUpdate():分配新標識儲存瞬時態物件;更新/重新關聯託管態物件。

以上兩個大家自己測試了!

結語

這篇初步知道了物件的狀態。雖然物件的狀態的細節由NHibernate自己維護,但是物件狀態在NHibernate應用中還是比較重要的。同時物件狀態也涉及了NHibernate快取、離線查詢等內容。

本系列連結:NHibernate之旅系列文章導航

NHibernate Q&A

下次繼續分享NHibernate!