[英]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
...
我找到了兩個臨時解決方案:
我不明白這種行為。 如果關系是單向或雙向的,為什么在處理上有所不同? 此外,如果我在同一事務的上下文中獲取這些實體,那么第一個 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() 方法。 無需使用持久標識符,所謂的“業務密鑰”要好得多。 是天生的鑰匙,不過這次用起來也沒什么錯!
不要覆蓋equals
和hashCode
。 Hibernate 有自己的實現來找出對象,這就是為什么你沒有得到預期的結果。 這篇文章解釋了更多: https : //community.jboss.org/wiki/EqualsandHashCode?_sscc=t
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.