休眠事实:等于和HashCode
每個Java對象都繼承了equals和hashCode方法,但它們僅對Value對象有用,對面向無狀態行為的對象毫無用處。
盡管使用“ ==”運算符比較引用很簡單,但是對于對象相等而言,事情要復雜一些。
由于您負責告訴平等性對特定對象類型的含義,因此必須使equals和hashCode實現遵循java.lang.Object JavaDoc( equals()和hashCode() )指定的所有規則。
了解您的應用程序(及其使用的框架)如何利用這兩種方法也很重要。
幸運的是,Hibernate不需要它們檢查實體是否已更改,為此具有專用的臟檢查機制。
瀏覽Hiberante文檔后,我偶然發現了這兩個鏈接: Equals和HashCode和Hiberante 4.3文檔指出了需要兩種方法的上下文:
- 將實體添加到Set集合時
- 將實體重新附加到新的持久性上下文時
這些要求來自Object.equals的“ consistent ”約束,使我們遵循以下原則:
在所有JPA對象狀態下,實體必須等于其自身 :
- 短暫的
- 附上
- 超脫
- 移除(只要該對象被標記為要移除并且它仍然駐留在堆上)
因此,我們可以得出以下結論:
具有在整個實體對象空間中唯一的屬性的那些實體字段通常稱為業務密鑰。
與合成數據庫自動遞增的ID相對,業務密鑰還獨立于我們的項目體系結構中采用的任何持久性技術。
因此,必須從我們創建實體的那一刻起就設置業務密鑰,然后再也不要更改它。
讓我們以實體相關性為例,并選擇適當的業務密鑰。
- 根實體用例(沒有任何父依賴項的實體)
這是實現equals / hashCode的方式:
@Entity public class Company {@Id@GeneratedValue(strategy = GenerationType.AUTO)private Long id;@Column(unique = true, updatable = false)private String name;@Overridepublic int hashCode() {HashCodeBuilder hcb = new HashCodeBuilder();hcb.append(name);return hcb.toHashCode();}@Overridepublic boolean equals(Object obj) {if (this == obj) {return true;}if (!(obj instanceof Company)) {return false;}Company that = (Company) obj;EqualsBuilder eb = new EqualsBuilder();eb.append(name, that.name);return eb.isEquals();} }名稱字段代表公司業務密鑰,因此被聲明為唯一且不可更新。 因此,如果兩個Company對象具有相同的名稱,則它們相等,而忽略了它可能包含的任何其他字段。
- 擁有EAGER的父級的子實體
在此示例中,我們始終會獲取產品的公司,并且由于產品代碼在公司之間并不唯一,因此我們可以在業務密鑰中包含父實體。 父引用被標記為不可更新,以防止違反equals / hashCode合同(將產品從一家公司轉移到另一家公司毫無意義)。 但是,如果“父級”具有“一組子級”實體,并且您調用類似以下內容的方法,則此模型將中斷
public void removeChild(Child child) {children.remove(child);child.setParent(null); }由于將父級設置為null,因此這將破壞equals / hashCode合同,并且如果子級對象是Set,則不會在子級集合中找到子對象。 因此,在使用具有此類equals / hashCode的Child實體的雙向關聯時要小心。
- 擁有LAZY父級的子實體
如果在沒有產品的情況下獲取圖像并且關閉了持久性上下文,并且將圖像加載到集合中,則將得到LazyInitializationException,如以下代碼示例所示:
List images = transactionTemplate.execute(new TransactionCallback<List>() {@Overridepublic List doInTransaction(TransactionStatus transactionStatus) {return entityManager.createQuery("select i from Image i ", Image.class).getResultList();} }); try {assertTrue(new HashSet(images).contains(frontImage));fail("Should have thrown LazyInitializationException!"); } catch (LazyInitializationException expected) {}因此,我不建議使用該用例,因為它容易出錯,并且要正確使用equals和hashCode,我們總是需要始終初始化LAZY關聯。
- 子實體不理父母
在此用例中,我們只需從業務密鑰中刪除父級引用即可。 只要我們始終通過“父級子代”集合使用“子代”,我們就很安全。 如果我們從多個父級加載子級,并且業務鍵在這些父級中不唯一,則不應將其添加到Set集合中,因為Set可能會丟棄來自不同父級的具有相同業務鍵的Child對象。
結論
為實體選擇正確的業務密鑰并不是一件容易的事,因為它反映了您在Hibernate范圍內外的實體使用情況。 使用實體之間唯一的字段組合可能是實現equals和hashCode方法的最佳選擇。
使用EqualsBuilder和HashCodeBuilder可以幫助我們編寫簡潔的equals和hashCode實現,并且似乎也可以與Hibernate Proxies一起使用。
翻譯自: https://www.javacodegeeks.com/2013/11/hibernate-facts-equals-and-hashcode.html
總結
以上是生活随笔為你收集整理的休眠事实:等于和HashCode的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 保时捷电脑桌面壁纸(保时捷壁纸,高清图片
- 下一篇: 何时以及如何使用ThreadLocal