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

Posted

技术标签:

【中文标题】@Transient 属性应该用在 equals/hashCode/toString 中吗?【英文标题】:Should @Transient property be used in equals/hashCode/toString? 【发布时间】:2011-02-26 10:40:23 【问题描述】:

我有 JPA 实体,其中一些属性用 @Transient 注释。

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

我的第一个想法是,但我不知道为什么。

提示? 想法? 解释?

【问题讨论】:

【参考方案1】:

我知道@Transienttransient 的两种典型用法是将它们用于无法序列化/持久化的东西(例如远程资源句柄 ) 或可以从其他人重构的计算属性。

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

对于不可序列化/可持久化的数据,视情况而定。我可以想象一个不可序列化的资源的句柄,但您仍然可以比较句柄代表的资源名称。 toString 也一样,打印句柄资源名称可能很有用。

这是我的 2 美分,但如果你解释一下你对 @Transient 的特殊用法,也许有人可以给出更好的建议。

【讨论】:

【参考方案2】:

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

不幸的是,这个解决方案有一个主要问题:当使用生成的标识符时,直到实体变为持久才会分配值,所以如果瞬态 em> 实体在保存之前被添加到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 的合同。业务键的属性不必像数据库主键一样稳定,您只需只要对象在同一个集合中,就必须保证稳定性。” - 12.1.3. Considering object identity

"建议您使用业务键相等来实现equals()hashCode()。业务键相等意味着equals() 方法仅比较形成业务键的属性。它是一个可以在现实世界中识别我们的实例的键(一个自然的候选键)” - 4.3. Implementing equals() and hashCode()

那么,回到最初的问题:

尽可能使用业务密钥。 @Transient 属性很可能不是此类键的一部分。 如果不可能,请使用标识符属性,但确保获取之前分配的值,以将实体添加到 ListMapSet

另见

Equals and HashCode Don't Let Hibernate Steal Your Identity equals and hashcode in Hibernate 理解 equals() 和 hashCode()(第 396 页)在 Java Persistence with Hibernate 中

【讨论】:

【参考方案3】:

异常可能来自让它成为transient,同时您提供writeObject()readObject() 来处理它。

【讨论】:

以上是关于@Transient 属性应该用在 equals/hashCode/toString 中吗?的主要内容,如果未能解决你的问题,请参考以下文章

@Transient

@Transient注解----Hiberbate

Transient关键字

学习关键字 transient

JPA之@Transient

transient在java中的作用