日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

休眠事实:等于和HashCode

發布時間:2023/12/3 编程问答 44 豆豆
生活随笔 收集整理的這篇文章主要介紹了 休眠事实:等于和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來比較對象,因為瞬態和附加的對象版本不會彼此相等。
  • 我們不能依賴默認的Object equals / hashCode實現,因為在兩個不同的持久性上下文中加載的兩個實體最終將成為兩個不同的Java對象,因此違反了全狀態相等性規則。
  • 因此,如果Hibernate使用相等性唯一地標識對象,則在整個生命周期中,我們需要找到滿足此要求的屬性的正確組合。
  • 具有在整個實體對象空間中唯一的屬性的那些實體字段通常稱為業務密鑰。

    與合成數據庫自動遞增的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的父級的子實體
    @Entity public class Product {@Id@GeneratedValue(strategy = GenerationType.AUTO)private Long id;@Column(updatable = false)private String code;@ManyToOne(fetch = FetchType.EAGER)@JoinColumn(name = "company_id", nullable = false, updatable = false)private Company company;@OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "product", orphanRemoval = true)@OrderBy("index")private Set images = new LinkedHashSet();@Overridepublic int hashCode() {HashCodeBuilder hcb = new HashCodeBuilder();hcb.append(name);hcb.append(company);return hcb.toHashCode();}@Overridepublic boolean equals(Object obj) {if (this == obj) {return true;}if (!(obj instanceof Product)) {return false;}Product that = (Product) obj;EqualsBuilder eb = new EqualsBuilder();eb.append(name, that.name);eb.append(company, that.company);return eb.isEquals();} }

    在此示例中,我們始終會獲取產品的公司,并且由于產品代碼在公司之間并不唯一,因此我們可以在業務密鑰中包含父實體。 父引用被標記為不可更新,以防止違反equals / hashCode合同(將產品從一家公司轉移到另一家公司毫無意義)。 但是,如果“父級”具有“一組子級”實體,并且您調用類似以下內容的方法,則此模型將中斷

    public void removeChild(Child child) {children.remove(child);child.setParent(null); }

    由于將父級設置為null,因此這將破壞equals / hashCode合同,并且如果子級對象是Set,則不會在子級集合中找到子對象。 因此,在使用具有此類equals / hashCode的Child實體的雙向關聯時要小心。

    • 擁有LAZY父級的子實體
    @Entity public class Image {@Id@GeneratedValue(strategy = GenerationType.AUTO)private Long id;@Column(updatable = false)private String name;@ManyToOne(fetch = FetchType.LAZY)@JoinColumn(name = "product_id", nullable = false, updatable = false)private Product product;@Overridepublic int hashCode() {HashCodeBuilder hcb = new HashCodeBuilder();hcb.append(name);hcb.append(product);return hcb.toHashCode();}@Overridepublic boolean equals(Object obj) {if (this == obj) {return true;}if (!(obj instanceof Image)) {return false;}Image that = (Image) obj;EqualsBuilder eb = new EqualsBuilder();eb.append(name, that.name);eb.append(product, that.product);return eb.isEquals();} }

    如果在沒有產品的情況下獲取圖像并且關閉了持久性上下文,并且將圖像加載到集合中,則將得到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一起使用。

    參考: Hibernate Fact:來自我們JCG合作伙伴 Vlad Mihalcea的Equals和HashCode ,位于Vlad Mihalcea的Blog博客中。

    翻譯自: https://www.javacodegeeks.com/2013/11/hibernate-facts-equals-and-hashcode.html

    總結

    以上是生活随笔為你收集整理的休眠事实:等于和HashCode的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。