JPA 实体 equals()/hashCode() 的另一种实现

问题描述 投票:0回答:1

JPA 实体

equals()
hashCode()
方法的历史很久以前就开始了。有大量的讨论,只需谷歌搜索“jpa equals hashcode”,你就会发现一篇精彩的Vlad Mihalcea文章,试图找到JPA Buddy团队的差距,这里有很多帖子在 S/O 等等。

当然,本文讨论的是没有业务密钥可以依赖的情况,唯一的选择就是使用自动生成的序列密钥。

首先,我想知道

  • 为什么Hibernate没有官方文档
  • 以及为什么 Lombok 不介入一些花哨的
    @EqualsAndHashCodeOfSyntheticId
    (相反,还有大量文章解释将常规
    @EqualsAndHashCode
    应用于实体是一个非常糟糕的主意。确实如此)

然后结合 Vlad 和 JPA Buddy 的解决方案,我将提出另一种解决方案供您评估

public abstract class AbstractSyntheticIdEntity {
    public abstract Long getId();

    @Override
    public final boolean equals(Object o) {
        if (this == o) return true;
        if (o == null) return false;

        if (effectiveClass(this) != effectiveClass(o)) return false;
        AbstractSyntheticIdEntity that = (AbstractSyntheticIdEntity) o;
        return getId() != null && getId().equals(that.getId());
    }

    @Override
    public final int hashCode() {
        return effectiveClass(this).hashCode();
    }

    @Override
    public String toString() {
        return String.format(
                "%s(id=%d)",
                effectiveClass(this).getSimpleName(),
                getId()
        );
    }

    private static Class<?> effectiveClass(Object obj) {
        return obj instanceof HibernateProxy
                ? ((HibernateProxy) obj).getHibernateLazyInitializer().getPersistentClass()
                : obj.getClass();
    }
}

缺点:

  • 它是 Hibernate 特有的(我想这不是一个大问题,说 JPA 我们通常指的是 Hibernate)
  • 使用继承
  • 假设
    id
    Long
    ,如果您将
    Long
    Integer
    混合用于 ids,则可以轻松参数化

优点:

  • 无需在任何地方放置完全相同的
    equals()
    hashCode()
    ,只需从中扩展你的实体即可
  • 它通过了 Vlad 的 IdEqualityTest(在开始讨论之前,我恳请您评估您对它的假设),这意味着不同的实体状态、代理、引用、集合等都被考虑在内
  • 基础
    toString
    实施不是强制性的,而是一种奖励。实体当然可以覆盖它(不过要避免隐式额外加载!)

任何反馈都将受到高度赞赏。

附注

如果缺点对你来说是一个障碍,我想没有什么比使用 Vlad 的解决方案更好的了,并将这个片段放在 every 实体

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof CLASS_NAME)) return false;
        CLASS_NAME that = (CLASS_NAME) o;
        return id != null && id.equals(that.getId());
    }

    @Override
    public int hashCode() {
        return getClass().hashCode();
    }
hibernate jpa entity equals hashcode
1个回答
0
投票

另一种选择是考虑这种方法。

我相信实体(我的意思是带有

@Entity
@Id
注释的类的对象)应该 永远不会逃脱事务边界

通常它们已经或可能通过

LAZY
关联的代理得到增强。这种“魔法”只有在交易中才能正确发挥作用。 假设您知道默认的
spring.jpa.open-in-view=true
反模式,应更改为
false

所以,如果你

  • 不要让实体在交易之外使用
  • 不要使用
    em.detach()
  • 不要使用
    em.merge()
    (你是否清楚地了解为什么要使用它?)
  • 不要将实体与其代理进行比较(在交易之外)
  • 不要将不同上下文的实体混合在一起
  • ...实际上我正在以不同的方式写第一个声明

然后无需重写

equals()
hashCode()
方法的默认实现。一切都会正常工作。

附注事务边界之外的实体还如何使用?他们必须首先转换为 DTO。我建议使用 MapStruct,因为它是唯一具有 compile 转换检查功能的一个。

© www.soinside.com 2019 - 2024. All rights reserved.