簡體   English   中英

單向@OneToMany 關聯未通過 JPA 中的相等性測試

[英]Unidirectional @OneToMany association fails equality test in JPA

我已經建立了一個單向的 OneToMany 關系,就像 JPA 2.1 規范的第 2.10.5.1 節中的例子:

@Entity
public class Client implements Serializable {

...

    @OneToMany
    private List<ServiceOrder> activeServiceOrders;

    public void setActiveServiceOrders( List<ServiceOrder> activeServiceOrders ) {

        this.activeServiceOrders = activeServiceOrders;
    }

    public List<ServiceOrder> getActiveServiceOrders() {

        return activeServiceOrders;
    }
}

ServiceOrder 類使用其自動生成的 long id 實現 hashCode 和 equals。 它們是由 Eclipse 實現的。

public class ServiceOrder implements Serializable {

    @TableGenerator( name = "generator_serviceOrder", table = "SEQUENCE_TABLE", pkColumnName = "SEQ_NAME", valueColumnName = "LAST_VALUE_GEN", pkColumnValue = "SERVICE_ORDER_SEQ", allocationSize = 1, initialValue = 0 )
    @Id
    @GeneratedValue( strategy = GenerationType.TABLE, generator = "generator_serviceOrder" )
    private long id;
...
    @Override
    public boolean equals( Object obj ) {

        if ( this == obj )
            return true;
        if ( obj == null )
            return false;
        if ( getClass() != obj.getClass() )
            return false;
        ServiceOrder other = (ServiceOrder ) obj;
        if ( id != other.id )
            return false;
        return true;
    }
...
}

表格都是按預期自動生成的。 然后,當我想建立關系時,我會這樣做:

...
Client client = entityManager.find(...);
ServiceOrder so = entityManager.find(...);
client.getActiveServiceOrders().add( so );
...

到目前為止一切都很好,事務提交成功。 當我嘗試刪除關系(在另一個事務中,另一個時刻)時,問題就開始了:

...
Client sameClient = entityManager.find(...);
ServiceOrder sameSo = entityManager.find(...);
log.info(sameClient.getActiveServiceOrders().size()); // "1", OK
log.info(sameClient.getActiveServiceOrders().contains(so)); // "false". Why?
sameClient.getActiveServiceOrders().remove(so); // does nothing, returns false
...

我調試並發現以下在 ServiceOrder.equals() 中失敗:

...
if ( getClass() != obj.getClass() ) // different probably because JPA (Hibernate) proxies one of the objects
    return false; // returns
...

我找到了兩個臨時解決方案:

  1. 移除 ServiceOrder equals() 和 hashCode(); 或者
  2. 使關系雙向(當然,每次添加/刪除時都要更新雙方);

我不明白這種行為。 如果關系是單向或雙向的,為什么在處理上有所不同? 此外,如果我在同一事務的上下文中獲取這些實體,那么第一個 equals 測試將如何失敗:

if ( this == obj )
    return true;

我正在使用 JPA 2.1(Wildfly 8.1.0)。

最好的問候,並提前感謝您。 雷南

你應該覆蓋 equals 和 hashCode 但你不應該使用 ID 作為哈希碼,除非你使 hashCode 不可變並且只有當它不為 null時才使用 ID 作為 equals 。

否則,在保存 ID 為 null 的實體之前,當您將 Transient 實體添加到集合中時,將在刷新時間分配該實體,當它被持久化並生成 ID 時,equals/hashCode 合同將被破壞.

Hibernate 最佳實踐建議使用業務鍵來實現對象相等性/hashCode。

所以引用參考文檔:

一般約定是:如果您想將對象存儲在 List、Map 或 Set 中,那么需要實現 equals 和 hashCode,以便它們遵守文檔中指定的標准約定。

為了避免這個問題,我們建議使用持久類的“半”唯一屬性來實現 equals()(和 hashCode())。 基本上,您應該認為您的數據庫標識符根本沒有商業意義(請記住,無論如何建議使用代理標識符屬性和自動生成的值)。 數據庫標識符屬性應該只是一個對象標識符,並且基本上應該只被 Hibernate 使用。 當然,您也可以將數據庫標識符用作方便的只讀句柄,例如在 Web 應用程序中構建鏈接。

不使用數據庫標識符進行相等比較,您應該使用一組用於標識各個對象的 equals() 屬性。 例如,如果您有一個“Item”類並且它有一個“name”String 和“created”Date,我可以使用兩者來實現一個好的 equals() 方法。 無需使用持久標識符,所謂的“業務密鑰”要好得多。 是天生的鑰匙,不過這次用起來也沒什么錯!

不要覆蓋equalshashCode Hibernate 有自己的實現來找出對象,這就是為什么你沒有得到預期的結果。 這篇文章解釋了更多: https : //community.jboss.org/wiki/EqualsandHashCode?_sscc=t

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM