繁体   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