@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】:我知道@Transient
和transient
的两种典型用法是将它们用于无法序列化/持久化的东西(例如远程资源句柄 ) 或可以从其他人重构的计算属性。
对于计算数据,在等式关系 (equals/hashCode
) 中使用它们是没有意义的,因为这将是多余的。该值是根据已在等式中使用的其他值计算得出的。但是,将它们打印在 toString
中仍然有意义(例如,使用基本价格和比率来计算实际价格)。
对于不可序列化/可持久化的数据,视情况而定。我可以想象一个不可序列化的资源的句柄,但您仍然可以比较句柄代表的资源名称。 toString
也一样,打印句柄资源名称可能很有用。
这是我的 2 美分,但如果你解释一下你对 @Transient
的特殊用法,也许有人可以给出更好的建议。
【讨论】:
【参考方案2】:toString()
的情况不同,toString()
可以为所欲为,所以我只介绍equals()
(和hashCode()
)。
首先,规则:如果要将对象存储在List
、Map
或Set
中则要求实现equals
和hashCode
因此他们遵守文档中指定的标准合同。
现在,如何实现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
属性很可能不是此类键的一部分。
如果不可能,请使用标识符属性,但确保获取之前分配的值,以将实体添加到 List
、Map
、Set
。
另见
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 中吗?的主要内容,如果未能解决你的问题,请参考以下文章