[英]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.