java - 单向@OneToMany 关联未通过 JPA 中的相等性测试

标签 java hibernate jpa orm wildfly

我已经建立了一个单向的 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 不可变并仅使用 ID when it's not null for equality .

否则,在保存 ID 为 null 的实体之前,当您将 Transient 实体添加到集合时,将在刷新时间内分配该 ID,当它被持久化并生成 ID 时,equals/hashCode 契约是会坏掉的。

Hibernate 最佳实践建议 using a business key用于对象相等/hashCode。

所以引用引用文档:

The general contract is: if you want to store an object in a List, Map or a Set then it is a requirement that equals and hashCode are implemented so they obey the standard contract as specified in the documentation.

To avoid this problem we recommend using the "semi"-unique attributes of your persistent class to implement equals() (and hashCode()). Basically you should think of your database identifier as not having business meaning at all (remember, surrogate identifier attributes and automatically generated values are recommended anyway). The database identifier property should only be an object identifier, and basically should be used by Hibernate only. Of course, you may also use the database identifier as a convenient read-only handle, e.g. to build links in web applications.

Instead of using the database identifier for the equality comparison, you should use a set of properties for equals() that identify your individual objects. For example, if you have an "Item" class and it has a "name" String and "created" Date, I can use both to implement a good equals() method. No need to use the persistent identifier, the so-called "business key" is much better. It's a natural key, but this time there is nothing wrong with using it!

关于java - 单向@OneToMany 关联未通过 JPA 中的相等性测试,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24877814/

相关文章:

java - 如何将 jar 或 zip 文件以外的文件添加到 eclipse 中的任何项目构建路径中?

java - 在单独的机器上运行 bazel 远程执行程序测试

java - 为什么 Hibernate 会忽略 package-info.java?

java - 应用缓存对比hibernate二级缓存,用哪个?

java - JPA 在 Spring 中到底是如何工作的?一些疑问

java - 如何通过内部类itemListener修改类变量?

java - 使用java和apache commons CSV库在CSV文件中搜索 "Item"

java - Hibernate 忽略自动提交错误设置。创建本地连接

java - Hibernate:如何将 B 是 A 的属性进行一对一映射?

java - 如果表属性有默认值clock_timestamp(),如何注释实体中的属性?