【问题标题】:What is needed to determine equality of an @Entity class?确定@Entity 类的相等性需要什么?
【发布时间】:2017-07-31 04:49:50
【问题描述】:

我正在做一些 Springboot 开发,想知道是否只覆盖 equals 方法以确定相等性是否安全。具体来说,相等性依赖于Pet 类的petId 字段(使用@Entity 注释):

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "PET_ID", unique = true, nullable = false)
    private long petId;
   /*getters and setters*/

我对此进行了测试,它可以工作。这个想法是,使用这个唯一的数据库生成 ID,我可以使用相等性来确定是更新现有记录还是创建新记录,基于从 PUT 调用传入的包含正确格式的 Pet 对象的 petId 值在请求参数中,如下:

@CrossOrigin()
    @RequestMapping(value = "/equalitytest", method = RequestMethod.PUT, produces = MediaType.APPLICATION_JSON_VALUE)
    public @ResponseBody Pet getEquality(@RequestBody Pet inputPet) {

        Pet res = null;

        List<Pet> pets = petRepo.findAll();

        for (Pet p : pets) {

            if (inputPet.equals(p)) {
                res = p;
            }
        }

        return res;
    }

这是一种错误或低效的方法吗?我已经读过,当equals 被覆盖时,覆盖hashCode 始终是最佳做法,但我不知道在这种情况下是否需要这样做/应该如何实现。

谢谢。

【问题讨论】:

  • 始终同时覆盖equalshashCodetoStringcompareTo(如果使用)。我建议遵循良好的面向对象实践,使用自然键来表示相等。远离对象模型中的人工“id”字段。
  • 自然键本身可以更新时如何使用它们?例如,Pet 类中还有两个字段,称为 petNamepetOwner,并且可以根据与另一个对象的 petId 字段确定的相等性来更新其中的任何一个。

标签: java spring-boot spring-data-jpa


【解决方案1】:

如果您使用生成的 ID(这是您的情况),依赖数据库标识符比较(在您的情况下为 petId)可能会给您带来麻烦。想想看;在保存对象之前不会设置标识符值。只有在您的瞬态对象上调用session.save() 时才会设置它,使其持久化。

如果您使用手动分配的 ID,我们可以说您没有问题,您只需确保在将对象添加到集合之前设置标识符值。

此外,您必须考虑要为该特定实体/对象实现什么目标。您可以依靠其他字段(通常是这种情况)来查看该对象是否可以不同。

例如,假设您将 equalshashCode 合约基于实体 ID,并且您正在建模 Person {id, first_name, last_name, ssn} 对象。如果我为您提供相同的first_namelast_namessn 的两条记录……它们真的不相等吗?但是因为您只考虑实体 ID,所以不会。

关于覆盖equalshashCode,Hibernate has a nice(但很长)描述如何做以及何时做。

【讨论】:

  • 请查看我对控制器代码的编辑以了解相等性的用途。使用您编写的Person 示例,如果一个人的名字、姓氏和ssn 都必须更改怎么办?您将如何确定传入对象与数据库中已存在的对象是否相等?
  • 在这种情况下,您可以通过 ID 进行搜索,因为您特别想更新某些内容。就像我说的,这取决于你所说的“平等”。
  • 数据库中的 Int ID 是 代理键。它们不是用来识别与表行关联的实体。顾名思义,它们作为 自然键 的替代品,即建模域中的身份决定因素。它们的存在是为了加快数据库操作,而不是为身份建模。面对变化,它们并不耐用,所以放弃这条线。如果您阅读数据库理论,您会发现标识符问题有很多策略,没有一个是完美的。不过,有一件事很清楚。如果您无法使用域相关数据对相等性进行建模,那么代理 ID 将无济于事。
【解决方案2】:

我认为这不是错误或低效的方法。

下面,一个模型供你参考:

@Override
public int hashCode() {
    int hash = 0;
    hash += (componentId != null ? componentId.hashCode() : 0);
    return hash;
}

@Override
public boolean equals(Object object) {
    // TODO: Warning - this method won't work in the case the componentId fields are not set
    if (!(object instanceof Component)) {
        return false;
    }
    Component other = (Component) object;
    if ((this.componentId == null && other.componentId != null) || (this.componentId != null && !this.componentId.equals(other.componentId))) {
        return false;
    }
    return true;
}

@Override
public String toString() {
    return "com.example.model.Component[ id=" + componentId + " ]";
}

PS。 1:在我的例子中,模型被称为组件。

PS。 2: ...但是,为什么你想知道你的模型是否存在? Spring Boot 中的存储库关注方法 .save(Object object) 的此详细信息

希望能帮到你!

【讨论】:

  • 不幸的是,hashCode 不起作用,因为它在我的实体中属于 long 类型。
  • 感谢.save,它完全符合我的要求。我通过删除我编写的 equals 方法进行了测试,因此它使用了任何底层技术来查找和更新数据库中的适当记录。
  • @ITWorker,不客气!关于 Long 属性的哈希码的另一件小事...看看这个答案是否对您有帮助 stackoverflow.com/a/4045101/2638434
  • 您不可能根据实体的 ID 建立您的 equalshashCode 合同。当 ID 是自动生成的并且在首次创建对象时将是 null 时,这种方法甚至更少。尽管equals 合约说明了这一点,但它具有一些不需要/不需要的复杂性。
  • 您可以轻松地从long 中生成哈希,@ITWorker。例如:(int) ((id &gt;&gt; 23) ^ id),尽管这可能不是一个好方法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-07-08
  • 2019-07-10
  • 2012-04-03
  • 1970-01-01
  • 1970-01-01
  • 2014-09-09
  • 1970-01-01
相关资源
最近更新 更多