现在的位置: 首页 > 综合 > 正文

入門 16 – 物件狀態與識別入門 16 – 物件狀態與識別

2013年10月09日 ⁄ 综合 ⁄ 共 2284字 ⁄ 字号 评论关闭
在之前的主題大致瞭解Hibernate的基本操作與ORM之後,我們來重新探討一些Hibernate底層的一些機制,首先從Hibernate的物件狀態開始討論。

 Hibernate中的物件可以分為三種狀態:暫存(Transient)物件、持久(Persistent)物件、分離(Detached)物件。

 暫存物件指的是在Java程式流程中,直接使用new製作出之物件,例如在之前的例子中,User類別所衍生出之物件,在還沒有使用save()之前都是暫存物件,這些物件還沒有與持久層發生任何的關係,只要沒有名稱參考至該物件,該物件就會在適當的時候被回收。

 如果暫存物件使用save()或saveOrUpdate()方法將其狀態存儲至持久層,則此時暫存物件轉變為持久物件,持久物件會擁有與資料庫中相同的識別(identity),在我們對物件的設計中,也就是物件的id屬性所持有(被賦予)的值,會與資料庫識別(database identity)相同。如果是直接進行資料庫查詢所返回的資料物件,例如使用find()、get()、load()等方法查詢到的資料物件,這些物件都與資料庫中的欄位相關,具有與資料庫識別值相同的id值,它們也馬上變為持久物件。另外如果一個暫存物件被持久物件參考到,該物件也會自動變為持久物件,這是持久層管理員(Persistance Manager)自行透過演算查詢到關聯並作的操作,這之後的主題還會探討。

 持久物件如果在其上使用delete(),就會變回暫存物件,這是當然的,如果使用delete()後,資料庫中將沒有與該物件對應的欄位,物件與資料庫不再有任何的關聯。

 持久物件總是與session及transaction相關聯,在一個session中,對持久物件的改變不會馬上對資料庫進行變更,而必須在 transaction終止,也就是執行commit()之後,在資料庫中真正運行SQL進行變更,持久物件的狀態才會與資料庫進行同步,在同步之前的持久物件,我們稱其為dirty。

 當一個session執行close()或是clear()、evict()之後,持久物件會變為分離物件,這個時候的物件的id雖然擁有資料庫識別值,但它們目前並不在Hibernate持久層管理員的管理之下,它與暫存物件本質上是相同的,在沒有任何名稱參考至它們時,會在適當的時候被回收。

 分離物件擁有資料庫識別值,所以它可以透過update()、saveOrUpdate()、lock()等方法,再度與持久層關聯,通常的應用是,您從資料庫查詢一筆資料,關閉session以將連線資源讓出,此時物件是分離狀態,使用者經過一小段時候操作分離物件(像是購物車),然後重新開啟一個 session,將分離物件與session相關聯,然後將變更結果存回資料庫。

 大致瞭解物件狀態之後,我們還有討論一下物件識別問題,對資料庫而言,其識別一筆資料唯一性的方式是根據主鍵值,如果手上有兩份資料,它們擁有同樣的主鍵值,則它們在資料庫中代表同一個欄位的資料。對Java而言,要識別兩個物件是否為同一個物件有兩種方式,一種是根據物件是否擁有同樣的記憶體位置來決定,在Java語法中就是透過==運算來比較,一種是根據equals()、hasCode()中的定義。

 先探討第一種Java的識別方式在Hibernate中該注意的地方,在Hibernate中,如果是在同一個session中根據相同查詢所得到的相同資料,則它們會擁有相同的Java識別,舉個實際的例子來說明:

Session session = sessions.openSession();
Transaction tx = session.beginTransaction();
Object obj1 = session.load(User.class, new Long(007));
Object obj2 = session.load(User.class, new Long(007));
tx.commit();
session.close();

System.out.println(obj1 == obj2);

 上面這個程式片段將會顯示true的結果,表示obj1與obj2是參考至同一物件,但如果是以下的情況則不會顯示false:

Session session1 = sessions.openSession();
Transaction tx1 = session1.beginTransaction();
Object obj1 = session1.load(User.class, new Long(007));
tx1.commit();
session1.close();

Session session2 = sessions.openSession();
Transaction tx2 = session1.beginTransaction();
Object obj1 = session2.load(User.class, new Long(007));
tx2.commit();
session2.close();

System.out.println(obj1 == obj2);

 簡單的說,在兩個不同的session中,Hibernate不保證兩個物件擁有相同的參考。

 如果您想要比較兩個不同的session所得到的資料是否相同,您可以定義equals()與hasCode(),根據您的實際需求定義好,並使用obj1.equals(obj2)的方式來比較物件所真正擁有的值,下一個主題中我們再來討論相關的細節。

抱歉!评论已关闭.