当前位置: 首页 > 知识库问答 >
问题:

这种 JPA“缓存哈希代码”模式有什么问题吗?

吴升
2023-03-14

我在#hibernate IRC上,有人与我分享了以下(部分)模式

@Entity
public MyEntity() {

  ... primary key, object properties, getters/setters go here ...    

  @Column(nullable=false)
  private int hashCode;

  public MyEntity() {
     hashCode += id;
  }

  private final Set<String> tags = Sets.newHashSet();

  public void addTag(String tag) {
     if(tags.add(tag)) {
         hashCode += tag.hashCode();
     }
  }

  public void removeTag(String tag) {
     if(tags.remove(tag) {
        hashCode -= tag.hashCode();
     }
  }

  public void hashCode() {
    return hashCode;
  }

  ... http://www.artima.com/lejava/articles/equality.html style equals ...
}

人们可以称之为“分段更新的缓存hashCode”。(这绝对不是一些评论者认为的“业务密钥”模式。“业务密钥”模式需要一个带有唯一性约束的列,比如用于相等性测试的用户名)。

当在 JPA/Hibernate 中使用时,这意味着@Entity可以具有与 JBoss Equals 和 HashCode 文章中的“eq/hC with buisness [sic] key”类似的优势,但行为方式与开发人员期望的任何普通 Javabean 对象的行为方式(即不必像数据库行一样考虑对象):在持久化到数据库之前;在 EAGER 获取模式下的交易之后;以及随时在事务内或扩展模式下使用LAZY获取。

但是,确保hashCode始终正确更新可能是一个真正的挑战。

这里有人对这种模式有任何经验吗,你能分享你关于它的发现(正面和负面)吗?我对陷阱非常感兴趣,但我对那些声称某事是“坏的”而没有关于为什么它是坏的坚实论据的评论一点也不感兴趣。

请注意,我知道JPA hashCode()/equals()的困境,但我不相信这一模式实际上已经在讨论中讨论过了。

这种模式最初是为了避免在 @Entitys 中加载嵌套集合时出现问题而提出的,例如在使用 EAGER @ElementCollection 的 find() 上遇到 Hibernate LazyInitializationException。

更新:一些评论者对现有方法非常感兴趣。为了避免疑问,我只是对这个新模式的优点感兴趣。供您参考,也请您不要再说您认为等于/hashCode应该如何实现,请注意,我已经在我的@Entitys中使用了以下模式好几年了:

@Id
private UUID id = UUID.randomUUID();

@Override
public boolean equals(Object obj) {
    if (this == obj)
        return true;
    if (!(obj instanceof MY_CLASS) || id == null)
        return false;
    MY_CLASS other = (MY_CLASS) obj;
    return id.equals(other.id);
}

@Override
public int hashCode() {
    Preconditions.checkNotNull(id, "id must be set before @Entity.hashCode can be called");
    return id.hashCode();
}

我最近才尝试了一些新的东西,看看我是否真的需要一个像这样的独立方法

public boolean hasSameProperties(Note other) {
    Preconditions.checkNotNull(other);
    if (this == other)
        return true;
    return Objects.equal(source, other.source)
            && Objects.equal(title, other.title)
            && Objects.equal(tags, other.tags)
            && Objects.equal(contents, other.contents);
}

共有2个答案

慕铭
2023-03-14

我写了,重写了,删除了两三个长答案,解释了为什么我认为这是一个坏主意,但最终,所有这些都是对什么是对象同一性的基本解释,我相信你明白这一点。

我对这种模式的回应是,它不能解决任何需要解决的问题。

所谓的“hashCode/equals问题”的解决方案非常简单。在创建实体时给它们一个标识符。坚持下去。基于等于hashCode。不要依赖数据库或持久性层在插入时创建的标识符。这是一个非常古老的原则。

顺便说一句,对于实体来说,将等于hashCode基于可变字段从来都是不对的。实体的平等不是这样工作的。实体不仅仅是一个状态包,它们是一个身份,某个状态碰巧附加到这个身份上。平等和hashcode必须基于身份,而不是状态。

蒯华彩
2023-03-14

这个业务密钥不是唯一的,因此它似乎是equals()的一个糟糕的选择。

业务密钥 1002 添加了标记 500 和标记 -502,业务密钥 995 添加了标记 3 和标记 2?这些对象真的是平等的吗?

无论您的特定实体服务于什么目的,32位数相对较低的冲突风险都是可以忍受的,但是将某个东西称为“模式”时,我们希望它实际上是正确的,而不仅仅是对于给定的数据大小不太可能失败。

 类似资料:
  • 我试图做一个代码,将打印所有元素的内容与从一些链接,但它不起作用,我不知道为什么,这是代码: 当我粘贴“someurl”而不是

  • 提示:Extra content at the end of the document这样的错误

  • 嗨,我想知道如果你有你要寻找的对象的Hashcode,是否可以直接访问HashSet的内容,有点像在HashMap中使用Hashcode作为键。 我想它可能会像这样工作: 谢谢 编辑:谢谢你的回答。好的,我知道我可能会稍微推动HashSet的契约,但是对于这个特定的项目,等式完全由hashcode决定,我确信每个hashcode/hashbucket只有一个对象。我非常不愿意使用HashMap的原

  • 从原理到应用分析什么是哈希? 一、什么是哈希? 哈希(hash):将任意长度的输入(关键字),通过Hash算法变成固定长度的输出。这个映射的规则就是对应的Hash算法,而原始数据映射后的二进制串就是哈希值,通常哈希值代表了关键字的存储位置。 但是为什么要这样做呢?或者说,哈希是怎样来的呢? 哈希的出现解决了两个问题:存储和搜索。 1. 存储(数据结构):如果在容器中保存对象及其关联的键,并且不用键

  • 问题内容: 我了解到,哈希码是一个唯一标识参考号,它是一个十六进制数。 我的疑问是,参考数字代表对象的内存地址吗? 例如: 这段代码会返回我对象的内存地址吗? 问题答案: 哈希码不是唯一的标识。它只是一个数字,可以帮助您区分对象。两个不同的对象可能具有相同的哈希码,这很好。 HashCode特征: 如果obj1和obj2相等,则它们必须具有相同的哈希码。 如果obj1和obj2具有相同的哈希码,则

  • 问题内容: 与 是否有EventEmitter.call(this)所需的功能? 问题答案: 是否有EventEmitter.call(this)所需的功能? 显然,是的: 由于所有使用的方法都会检查其是否存在,因此,如果您忽略了调用,我希望不会有太大的改变,但是我不确定将来是否成立。 有迹象表明,做足够多的其他构造 不 容忍被省略,所以这是很好的做法,只是 始终 构造一个实例时调用构造函数。