3

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

@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 中使用时,这意味着can 具有与JBoss Equals 和 HashCode 文章@Entity中的“eq/hC with buisness [sic] key”类似的优势,但其行为方式与开发人员期望的任何普通 Javabean 对象的行为方式相同(即不必像数据库行一样考虑对象):在持久化到数据库之前;在获取模式之后;并随时在 a或模式中使用fetch 。TransactionEAGERLAZYTransactionEXTENDED

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

这里有没有人对这种模式有任何经验,你能分享你对它的发现(积极的和消极的)吗?我对 Gotchas 非常感兴趣,但我对那些声称某些东西“不好”的评论完全不感兴趣,而没有关于它为什么不好的可靠论据。

请注意,我知道The JPA hashCode() / equals() dilemma,但我不相信该模式实际上已包含在该讨论中。

最初建议使用此模式作为Collection在 s 中加载嵌套 s时避免问题的一种方法@Entity,例如在带有 EAGER @ElementCollection 的 find() 上的 Hibernate LazyInitializationException中遇到的问题。

更新:一些评论者对现有方法充满热情。为免生疑问,我只是对这种新模式的优点感兴趣。供您参考,也请您不要再说您认为应该如何实现 equals/hashCode,请注意,我已经为我@Entity的 s 使用了以下模式好几年了:

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

2 回答 2

1

此业务密钥不是唯一的,因此对于 equals() 来说似乎是一个糟糕的选择。

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

对于您的特定实体所服务的任何目的而言,32 位数字的冲突风险相对较低可能是可以容忍的,但是将某些东西称为“模式”,人们希望它实际上是正确的,而不仅仅是对于给定的任意不可能失败数据大小。

于 2012-07-30T22:23:55.603 回答
0

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

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

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

顺便说一句,对于实体来说,基于可变字段或基于可变字段是不对的。这不是平等对实体起作用的方式。实体不仅仅是一袋状态,它们是某种状态恰好附加到的身份。平等和哈希码必须基于身份,而不是状态。equalshashCode

于 2012-07-31T08:05:03.817 回答