4.1.6 實現equals()和hashCode()

 

在java中,equals()方法通常被應用程序調用或者被集合調用。例如,Set會調用equals()方法來確定元素是否重複。

首先,我們來考慮equals()的默認實現。Hibernate保證在每個Session中,每個表格行有且只有一個對象與其對應。如果你沒有從其他的Session中取得對象的話,那麼默認的equals方法應該是有效的。一旦你擁有被多個Session共享的對象的話,那麼在一個Set中就可能存在多個對象對應同一行數據的情況。這在語義上明顯是錯誤的。當然,如果你經驗豐富的話,可以構建一個複雜的程序來保證多個Session使用detached對象的正確性。這麼做的好處就是你無需再另外實現equals()方法了。

當然,如果這種相等的概念不是你想要的,那麼你就需要重寫equals()方法了。請記住當你重寫equals()方法的時候,記得也要重寫hashCode()方法。讓我們來看看一些重寫equals()和hashCode()的方法。

使用數據庫標識符

一種聰明的做法就是比較數據庫的標識符來實現equals()方法:

請注意當對象編程transient實例的時候,equals方法如何變化,因爲在transient狀態下,數據庫標識符仍未進行分配。

不幸的是,這種方法與一個巨大的缺陷:直到一個實體被保存的時候Hibernate纔會分配數據庫標識符。因此,如果一個對象在未保存之前就被加入到了Set,那麼加入到Set的時候其Hashcode會發生變化。這回導致餓cascade保存。我們不建議使用這種方法來判斷相等性。

值比較

一種更好的做法除了數據庫標識符屬性,在equals方法包含所有屬性的比較。這是大部分人認爲的equals方法,我們稱之爲的值比較。

當我們說所有屬性的時候,其實並不包括集合。集合一般對應着另外一個表,因此包含它是錯誤的。更重要的是,你不會想要所有的對象都去執行equals()方法的。在User的例子中,這就意味着你不能包含items集合。因此,下面的實現是你可能用到的:

然而,這種方法仍然存在兩個問題:

1)在不同Session中的對象可能不相等,如果其中的一個被修改了的話

2)對應不同數據庫實體的實例可能被認爲是相等的,除非有能夠保證屬性唯一的方法。

爲了找到解決辦法,你需要明白業務主鍵的意義。

業務主鍵

首先它是一個屬性或者是多個屬性的組合,對於同一個數據庫實體來說它是唯一的。與數據庫主鍵不同,它可以改變,只要不經常改變就可以了。

我們建議每個實體都需要一個業務主鍵,就算它包含對象中的所有屬性。

業務主鍵的比較說明了equals方法僅僅需要比較那些需要比較的屬性。這看起來是一個完美的解決方案。唯一的缺點就是需要仔細考慮來決定哪些屬性來做爲業務主鍵。

對於User類來說,username是一個候選的業務主鍵。它不能爲null,並且唯一,而且很少改動。

對於一些其他的類,業務主鍵可能變得更加複雜,它可能有許多屬性組合而成。例如,Bid類的業務主鍵可能是item ID的總和,或者再加上日期以及時間。

你可能會注意到equals()以及hashCode()方法經常會通過getter方法來訪問屬性。這很重要,因爲其他對象可能是一個代理對象,並不一定會真的維持一個持久狀態。這是Hibernate中不透明的一點,但是通過getter方法來訪問對象確實是一個更好的辦法。

最後,注意當需要修改業務主鍵的時候,要格外小心;不要修改那些在Set中的領域模型。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章