本節內容
引入
對象狀態
對象狀態轉換
結語
引入
在程序運行過程中使用對象的方式對數據庫進行操作,這必然會產生一系列的持久化類的實例對象。這些對象可能是剛剛創建並准備存儲的,也可能是從數據庫中查詢的,為了區分這些對象,根據對象和當前會話的關聯狀態,我們可以把對象分為三種:
瞬時對象:對象剛剛建立。該對象在數據庫中沒有記錄,也不在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()
1.瞬時態轉換持久態
{
if (_session.IsOpen)
_session.Close();
_session = _sessionManager.GetSession();
_transaction.Session = _session;
}
方法一: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]
3.托管態轉換持久態
public void PersistentConvertDetachedCloseTest()
{
Customer customer = _transaction.GetCustomerById(1);
Assert.IsTrue(_session.Contains(customer));
ResetSession();
Assert.IsFalse(_session.Contains(customer));
}
方法一: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緩存、離線查詢等內容。
出處:http://lyj.cnblogs.com