Hibernate 物品识别

Hibernate 物件识别對Java而言,要識別兩個物件是否為同一個物件有兩種方式,一種是根據物件是否擁有同樣的

Hibernate 物件识别

對Java而言,要識別兩個物件是否為同一個物件有兩種方式,一種是根據物件是否擁有同樣的記憶體位置來決定,在Java語法中就是透過== 運算來比較,這是Java所定義的物件識別(Object identity),一種是根據equals()、hasCode()中的定義,這是Java所定義的物件相等(Object equality)。

    物件識別先探討第一種Java的識別方式在Hibernate中該注意的地方,在Hibernate中,如果是在同一個session中根據相同查詢所得到的相同 資料,則它們會擁有相同的Java識別,舉個實際的例子來說明:?
    Session session = sessions.openSession();
    Object obj1 = session.load(User.class, new Integer(1));
    Object obj2 = session.load(User.class, new Integer(1));
    session.close();

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

    上面這個程式片段將會顯示true的結果,表示obj1與obj2是參考至同一物件,但如果是以下的情況則會顯示false:?
    Session session1 = sessions.openSession();?
    Object obj1 = session1.load(User.class, new?new Integer(1));?
    session1.close();?

    Session session2 = sessions.openSession();?
    Object obj2 = session2.load(User.class,?new Integer(1));?
    session2.close();?

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

    原因可以參考?簡介快取(Session Level)?。

    應用程式中基於效能的原因,不會在一個使用者的長時間操作會話階段,持續開始Session,將物件維持在 Persistence狀態,Hibernate並不保證不同時間所取得的資料物件,其是否參考至記憶體的同一位置,使用==來比較兩個物件的資料是否代 表資料庫中的同一筆資料是不可 行的。

    ?

      物件相等再來討論物件相等的問題,在Java程式中要比較兩個物件是否相同,會透過equals()方法,而Object預設的 equals()本身是比較物件的記憶體參考,如果您要比較兩個物件的資料內容是否相同,您必須實作 equals()與hashCode(),最簡單的實作方式,是在equals()中,對物件的每個屬性逐一加以比較是否相同,稱之為by value equality。

        資料識別討論一下Hibernate中資料識別問題,對資料庫而言,其識別一筆資料唯一性的方式是根據主鍵值,如果手上有兩份資料,它們擁有同樣的主鍵值,則它們在資料庫中代表同一個 欄位的資料。

        由於主鍵值是資料庫中的資料唯一識別方式,因此Hibernate中的資料物件是否對應於一筆欄位資料,就是根據與主鍵值對應的物件識別屬 性(identifier property),Hibernate會維護物件的識別屬性,必要時,您可以將識別屬性的setter方法設定為private,以避免程式中遭到 修改,您可以藉由Session的getIdentifier()方法取得物件的識別屬性值。

        如果要結合equals()、hashCode()來實作物件相等,一個根據資料庫的識別屬性的實作方式,是透過識別屬性的getter方法取得物件的識別屬性值並加以比較, 例如若id的型態是String,一個實作的例子如下:?
        public class User {?
        ? ? ....?

        ? ? public boolean equals(Object o) {?
        ? ? ? ? if(this == o) return true;?
        ? ? ? ? if(id == null || !(o instanceof User)) return false;?

        ? ? ? ? final User user == (User) o;?
        ? ? ? ? return this.id.equals(user.getId());?
        ? ? }?

        ? ? public int hashCode() {?
        ? ? ? ? return id == null ? System.identityHashCode(this) : id.hashcode();?
        ? ? }?
        }

        這個例子取自於Hibernate in Action第123頁的範例,稱之為database identity equality,然而要注意的是,因為當一個物件被new出來而還沒有save()時,它並不會被賦予id值,如果您在物件儲存前,可能就有需求比較物件的相等性,就不適用這 個方法,例如物件若會被加入Set物件之中,該物件在被儲存至資料庫前與後,在Set中的判斷將有所不同,導致明明是同一個物件,卻使得程式出現不同的行為。?

        較好的方式是根據商務鍵值(Business key)來實作equals()與hashCode(),稱之為business key equality,在?Hibernate 官方參考手冊?中給了一個例子:?
        public class Cat {?

        ? ? ...?
        ? ? public boolean equals(Object other) {?
        ? ? ? ? if (this == other) return true;?
        ? ? ? ? if (!(other instanceof Cat)) return false;?

        ? ? ? ? final Cat cat = (Cat) other;?

        ? ? ? ? if (!getName().equals(cat.getName())) return false;?
        ? ? ? ? if (!getBirthday().equals(cat.getBirthday())) return false;?

        ? ? ? ? return true;?
        ? ? }?

        ? ? public int hashCode() {?
        ? ? ? ? int result;?
        ? ? ? ? result = getName().hashCode();?
        ? ? ? ? result = 29 * result + getBirthday().hashCode();?
        ? ? ? ? return result;?
        ? ? }?

        }

        by value equality的實作方式類似,但根據性的不同是不再比對所有的屬性,而是只比較商務鍵,商 務鍵是一個屬性或多個屬性的結合,對每個具有相同資料庫識別的物件來說,商務鍵的組合也是唯一的,商務鍵的挑選可以找那些從不為null、 immutable或很少改變且具有唯一性的屬性(例如對應欄位中UNIQUE的屬性),選用識別屬性作為商務屬性之一也是一種選擇。

        願意的話,還可以使用org.apache.commons.lang.builder.EqualsBuilder與 org.apache.commons.lang.builder.HashCodeBuilder來協助定義equals()與hashCode(), 例如:
        package onlyfun.caterpillar;
        import org.apache.commons.lang.builder.EqualsBuilder;
        import org.apache.commons.lang.builder.HashCodeBuilder;

        public class User {
        ? ? ?....
        ??? public boolean equals(Object obj) {
        ??????? if(obj == this) {
        ??????????? return true;
        ??????? }
        ????????
        ??????? if(!(obj instanceof User)) {
        ??????????? return false;
        ??????? }
        ????????
        ??????? User user = (User) obj;
        ??????? return new EqualsBuilder()
        ???????????????? .append(this.name, user.getName())
        ???????????????? .append(this.phone, user.getPhone())
        ???????????????? .isEquals();
        ????????
        ??? }
        ????
        ??? public int hashCode() {
        ??????? return new HashCodeBuilder()
        ???????????????? .append(this.name)
        ???????????????? .append(this.phone)
        ???????????????? .toHashCode();
        ??? }
        }