我已经建立了一个单向的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
...
我找到了两个临时解决方案:
我不明白这种行为。如果这种关系是单向的还是双向的,为什么治疗上存在差异?此外,如果我在同一个事务的上下文中获取这些实体,那么第一个等于测试的失败方式如何:
if ( this == obj )
return true;
我使用的是JPA 2.1(Wildfly 8.1.0)。
最诚挚的问候,并提前感谢您。 Renan的
答案 0 :(得分:3)
你应该覆盖equals和hashCode,但是除非你使hashCode成为不可变的,否则你永远不应该使用该ID,只有当它不是等于null时才使用ID,如in this article所述。
否则,在保存在刷新时间期间要分配的ID为null的实体之前,当您将一个Transient实体添加到集合时,当它被持久化并且生成ID时,equals / hashCode合约是破碎了。
Hibernate最佳实践建议using a business key用于对象相等/ hashCode。
所以引用参考文档:
一般合同是:如果要将对象存储在List,Map中 或者一个Set然后它是equals和hashCode的要求 实施,以便他们遵守规定的标准合同 文档。
为避免此问题,我们建议使用“半” - 唯一属性 你的持久化类实现equals()(和hashCode())。 基本上你应该认为你的数据库标识符没有 商业意义(记住,代理标识符属性和 无论如何都推荐自动生成的值)。数据库 标识符属性应该只是一个对象标识符,基本上 应该只由Hibernate使用。当然,你也可以使用 数据库标识符作为方便的只读句柄,例如建立 Web应用程序中的链接。
而不是使用数据库标识符进行相等 比较,你应该使用equals()的一组属性 识别您的个人对象。例如,如果你有一个“项目” 类和它有一个“名称”字符串和“创建”日期,我可以使用两者 实现一个好的equals()方法。无需使用持久性 标识符,所谓的“业务密钥”要好得多。它是 自然键,但这次使用它没有错!
答案 1 :(得分:0)
请勿覆盖equals
和hashCode
。 Hibernate有自己的实现来查找对象,这就是为什么你没有得到预期的结果。
本文解释更多:
https://community.jboss.org/wiki/EqualsandHashCode?_sscc=t