是否有与此JPA“缓存的hashCode”模式任何陷阱?(Are there any gotchas

2019-09-20 12:16发布

I was on #hibernate IRC and somebody shared the following (partial) pattern with me

@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 ...
}

One could call this a "cached hashCode which is updated piecewise". (This is definitely NOT a "business key" pattern as some commenters seem to believe. The "business key" pattern requires a column with a uniqueness constraint on it, like a username, which is used for equality tests).

When used in JPA/Hibernate, it means that the @Entity can has similar advantages to "eq/hC with buisness [sic] key" from the JBoss Equals and HashCode article, yet behave the way a developer would expect any normal Javabean object to behave (i.e. without having to think about the object like a database row): before being persisted to the DB; after a Transaction in EAGER fetching modes; and at any time with LAZY fetch inside a Transaction or in EXTENDED mode.

However, ensuring that the hashCode is always updated correctly could be a real challenge.

Does anybody here have any experience with this pattern and could you share your discoveries (both positive and negative) about it? I'm extremely interested in Gotchas but I am not at all interested in comments which make claims that something is "bad" without solid arguments about why it is bad.

Note that I am aware of The JPA hashCode() / equals() dilemma, but I don't believe this pattern was actually covered in that discussion.

This pattern was originally suggested as a way to avoid a problem when loading nested Collections in @Entitys, such as encountered in Hibernate LazyInitializationException on find() with EAGER @ElementCollection.

UPDATE: some commenters are getting very passionate about the existing approaches. For the avoidance of doubt, I am just interested in the merits of this new pattern. For your reference, and to also ask you to stop saying how you believe equals/hashCode should be implemented, note that I have used the following pattern for my @Entitys for several years now:

@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();
}

and I only recently tried something new to see if I really needed to have a separate method like this

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);
}

Answer 1:

这项业务的关键不是唯一的,所以它看起来像一个糟糕的选择了equals()方法。

业务重点1002标签500,并添加到它的标签-502,业务键995具有标签3和标签2加入到了吗? 那些对象真正的意思是一样的吗?

碰撞的32个号码相对低风险是可以容忍不管出于什么目的的特定实体的供应,但成是指作为一个“模式”一个想它实际上是正确的,不只是随意不可能失败,对于一个给定数据大小。



Answer 2:

我写,改写和删除两个或三个较长的答案,解释为什么我认为这是一个坏主意,但最终,所有任何他们都是什么对象标识是基本的解释,我敢肯定,你明白的。

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

被指控的解决方案“的hashCode /等于问题”是难以置信的简单。 当你创建它们给你的实体的标识符。 坚持它。 基本equalshashCode就可以了。 依赖于通过在插入数据库或持久层创建的标识符。 这是现在正古老的原则 。

顺便说一句,对于实体,这是从来没有正确的基础或者equalshashCode上可变的领域。 这不是平等如何作用于实体。 实体不只是国家的一个袋子,他们对其中一些国家恰好是附加的身份。 平等和hashCode必须基于身份,而不是国家。



文章来源: Are there any gotchas with this JPA “cached hashCode” pattern?