@Transient属性应该在equals/hashCode/toString中使用吗?

10

我有一些带有@Transient注解的JPA实体属性。

equals/hashCode/toString方法中,我应该使用这些属性吗?

我的第一反应是不应该用,但我不知道原因。

  • 有什么提示吗?
  • 有什么想法吗?
  • 有什么解释吗?
3个回答

8
toString() 的情况不同,你可以随心所欲地使用 toString(),因此我只会涵盖 equals()(和 hashCode())。
首先,规则是:如果你想在 ListMapSet 中存储一个对象,则有一个要求,即必须实现 equalshashCode,以便它们遵守文档中指定的标准契约。
现在,如何实现 equals()hashCode()?一个“自然”的想法是将映射为 Id 的属性作为 equals() 的一部分。
public class User {
    ...
    public boolean equals(Object other) {
        if (this==other) return true;
        if (id==null) return false;
        if ( !(other instanceof User) ) return false;
        final User that = (User) other;
        return this.id.equals( that.getId() );
    }
    public int hashCode() {
        return id==null ? System.identityHashCode(this) : id.hashCode();
  }
}

很不幸,这个解决方案存在一个主要问题:当使用生成的标识符时,值只有在实体变成持久化状态后才会被分配。因此,如果在保存之前将瞬态实体添加到Set中,则其哈希码将在Set中的时间内发生变化,这会违反Set的契约。
因此,建议的方法是使用作为业务键的属性,即一组对于具有相同数据库标识的每个实例都是唯一的属性组合。例如,对于User类,这可以是用户名:
public class User {
    ...
    public boolean equals(Object other) {
        if (this==other) return true;
        if ( !(other instanceof User) ) return false;
        final User that = (User) other;
        return this.username.equals( that.getUsername() );
    }
    public int hashCode() {
        return username.hashCode();
  }
}

Hibernate参考文档总结如下: 永远不要使用数据库标识符来实现相等性;使用业务键,这是一组唯一的、通常是不可变的属性的组合。如果将瞬态对象转换为持久对象,则数据库标识符将发生更改。如果将瞬态实例(通常与分离实例一起)保存在Set中,则更改hashcode会破坏Set的契约。业务键的属性不必像数据库主键那样稳定,您只需要保证对象在同一Set中时具有稳定性。——12.1.3. Considering object identity 建议使用业务键相等性来实现equals()hashCode()业务键相等性意味着equals()方法仅比较形成业务键的属性。它是在现实世界中标识我们实例的关键(自然候选键)。——4.3. Implementing equals() and hashCode()

那么,回到最初的问题:

  • 如果可能,使用业务键。 @Transient 属性很可能不是这样的键的一部分。
  • 如果不可能,使用标识属性,但确保在将实体添加到 ListMapSet 之前分配值。

另请参阅


0

@Transienttransient的两种典型用法我知道的是,要么用于不能被序列化/持久化的东西(例如远程资源句柄),要么用于可以从其他属性重建的计算属性。

对于计算数据,在等式关系(equals/hashCode)中使用它们没有意义,因为那会是多余的。该值是由其他已在等式中使用的值计算出来的。但仍然有可能在toString中打印它们(例如基础价格和比率用于计算实际价格)。

对于不可序列化/可持久化数据,情况取决于具体情况。我可以想象一个不可序列化的资源句柄,但您仍然可以比较句柄所代表的资源名称。同样,在toString中,打印句柄资源名称可能也是有用的。

这是我的个人看法,但如果您解释一下@Transient的具体用途,或许有人能给出更好的建议。


0

异常可能来自于将其设置为transient,同时您提供了writeObject()readObject()用于处理它。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接