[英]Should @Transient property be used in equals/hashCode/toString?
我有JPA实体,其中一些属性使用@Transient
注释。
我应该在equals/hashCode/toString
方法中使用这些属性吗?
我的第一个想法是不,但我不知道为什么。
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();
}
}
不幸的是,这个解决方案有一个主要问题 :当使用生成的标识符时,在实体变为持久化之前不会分配值,因此如果在保存之前将一个瞬态实体添加到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。 考虑对象身份“ 建议您使用Business键相等来实现
equals()
和hashCode()
。业务键相等性意味着equals()
方法仅比较构成业务键的属性。它是一个用于标识我们的实例的键。现实世界(自然候选键)“ - 4.3。 实现equals()和hashCode()
那么,回到最初的问题:
@Transient
属性很可能不属于这样一个键。 List
, Map
, Set
之前获取指定的值。 异常可能来自于让它是transient
,同时你提供writeObject()
和readObject()
处理它。
我所知道的@Transient
和transient
的两个典型用法是将它们用于无法序列化/持久化的东西(例如远程资源句柄 )或可以从其他人重建的计算属性。
对于计算数据 ,在等式关系( equals/hashCode
)中使用它们是没有意义的,因为它将是多余的。 该值是从已在相等中使用的其他值计算出来的。 然而,在toString
打印它们仍然是有意义的(例如,基本价格和比率用于计算实际价格)。
对于不可序列化/可持久化的数据 ,它取决于。 我可以想象一个不可序列化的资源的句柄,但你仍然可以比较句柄所代表的资源名称。 对于toString
,也许打印句柄资源名称很有用。
这是我的2美分,但如果你解释你对@Transient
的特殊用法,有人可以提供更好的建议。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.